Base Template

This commit is contained in:
2022-06-15 15:15:49 +07:00
parent 077e678979
commit f18bb85b94
47 changed files with 2597 additions and 1172 deletions

View File

@@ -3,3 +3,5 @@ GENERATE_SOURCEMAP=false
PORT=8083
REACT_APP_HOST_API_URL="http://localhost:8000"
VITE_API_URL="http://localhost:8000/api"

View File

@@ -0,0 +1,3 @@
GENERATE_SOURCEMAP=false
VITE_API_URL="https://aso-api.linksehat.dev/api"

View File

@@ -1,8 +1,4 @@
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
</IfModule>

View File

@@ -0,0 +1,22 @@
{
"0 debug pnpm:scope": {
"selected": 1
},
"1 error pnpm": {
"code": "ELIFECYCLE",
"errno": "ENOENT",
"syscall": "spawn",
"file": "sh",
"pkgid": "@minimal/material-kit-react@3.2.0",
"stage": "start",
"script": "vite",
"pkgname": "@minimal/material-kit-react",
"err": {
"name": "pnpm",
"message": "@minimal/material-kit-react@3.2.0 start: `vite`\nspawn ENOENT",
"code": "ELIFECYCLE",
"stack": "pnpm: @minimal/material-kit-react@3.2.0 start: `vite`\nspawn ENOENT\n at ChildProcess.<anonymous> (/home/dell/.nvm/versions/node/v16.13.0/pnpm-global/5/node_modules/.pnpm/pnpm@7.0.0/node_modules/pnpm/dist/pnpm.cjs:93294:22)\n at ChildProcess.emit (node:events:390:28)\n at maybeClose (node:internal/child_process:1064:16)\n at Process.ChildProcess._handle.onexit (node:internal/child_process:301:5)"
}
},
"2 warn pnpm:global": " Local package.json exists, but node_modules missing, did you mean to install?"
}

View File

@@ -8,7 +8,7 @@
"lint": "eslint --ext .ts,.tsx ./src",
"lint:fix": "eslint --fix --ext .ts,.tsx ./src",
"start": "vite",
"build": "vite build && cp .htaccess build/.htaccess && cp -r build ../../public/dashboard",
"build": "vite build --mode production && cp .htaccess build/.htaccess && rm -f -r ../../public/client-portal && cp -r build ../../public/client-portal",
"serve": "vite preview",
"clear-all": "rm -rf build node_modules",
"re-start": "rm -rf build node_modules && yarn install && yarn start",
@@ -44,6 +44,7 @@
"@iconify/react": "^3.2.1",
"@mui/lab": "5.0.0-alpha.80",
"@mui/material": "^5.6.4",
"@mui/icons-material": "^5.8.0",
"@mui/system": "^5.6.4",
"@mui/x-data-grid": "^5.10.0",
"@vitejs/plugin-react": "^1.3.2",
@@ -52,6 +53,7 @@
"date-fns": "^2.28.0",
"framer-motion": "^6.3.3",
"history": "^5.3.0",
"jsx-runtime": "^1.2.0",
"lodash": "^4.17.21",
"notistack": "^2.0.4",
"nprogress": "^0.2.0",

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 543 B

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@@ -0,0 +1,66 @@
import { UserCredential } from 'firebase/auth';
// ----------------------------------------------------------------------
export type ActionMap<M extends { [index: string]: any }> = {
[Key in keyof M]: M[Key] extends undefined
? {
type: Key;
}
: {
type: Key;
payload: M[Key];
};
};
export type AuthUser = null | Record<string, any>;
export type AuthState = {
isAuthenticated: boolean;
isInitialized: boolean;
user: AuthUser;
};
export type JWTContextType = {
isAuthenticated: boolean;
isInitialized: boolean;
user: AuthUser;
method: 'jwt';
login: (email: string, password: string) => Promise<void>;
register: (email: string, password: string, firstName: string, lastName: string) => Promise<void>;
logout: () => Promise<void>;
};
export type FirebaseContextType = {
isAuthenticated: boolean;
isInitialized: boolean;
user: AuthUser;
method: 'firebase';
login: (email: string, password: string) => Promise<UserCredential>;
register: (email: string, password: string, firstName: string, lastName: string) => Promise<void>;
logout: () => Promise<void>;
};
export type AWSCognitoContextType = {
isAuthenticated: boolean;
isInitialized: boolean;
user: AuthUser;
method: 'cognito';
login: (email: string, password: string) => Promise<unknown>;
register: (
email: string,
password: string,
firstName: string,
lastName: string
) => Promise<unknown>;
logout: VoidFunction;
};
export type Auth0ContextType = {
isAuthenticated: boolean;
isInitialized: boolean;
user: AuthUser;
method: 'auth0';
login: () => Promise<void>;
logout: VoidFunction;
};

View File

@@ -0,0 +1,55 @@
export type NewPostFormValues = {
title: string;
description: string;
content: string;
cover: File | any;
tags: string[];
publish: boolean;
comments: boolean;
metaTitle: string;
metaDescription: string;
metaKeywords: string[];
};
export type PostComment = {
id: string;
name: string;
avatarUrl: string;
message: string;
postedAt: Date;
users: {
id: string;
name: string;
avatarUrl: string;
}[];
replyComment: {
id: string;
userId: string;
message: string;
postedAt: Date;
tagUser?: string;
}[];
};
export type Post = {
id: string;
cover: string;
title: string;
description: string;
createdAt: Date | string | number;
view: number;
comment: number;
share: number;
favorite: number;
author: {
name: string;
avatarUrl: string;
};
tags: string[];
body: string;
favoritePerson: {
name: string;
avatarUrl: string;
}[];
comments: PostComment[];
};

View File

@@ -0,0 +1,14 @@
import { EventInput } from '@fullcalendar/common';
// ----------------------------------------------------------------------
export type CalendarView = 'dayGridMonth' | 'timeGridWeek' | 'timeGridDay' | 'listWeek';
export type CalendarState = {
isLoading: boolean;
error: Error | string | null;
events: EventInput[];
isOpenModal: boolean;
selectedEventId: null | string;
selectedRange: null | { start: Date; end: Date };
};

View File

@@ -0,0 +1,65 @@
// ----------------------------------------------------------------------
export type Contact = {
id: string;
name: string;
username: string;
avatar: string;
address: string;
phone: string;
email: string;
lastActivity: Date | string | number;
status: string;
position: string;
};
export type Participant = {
id: string;
name: string;
username: string;
avatar: string;
address?: string;
phone?: string;
email?: string;
lastActivity?: Date | string | number;
status?: 'online' | 'offline' | 'away' | 'busy';
position?: string;
};
export type TextMessage = {
id: string;
body: string;
contentType: 'text';
attachments: string[];
createdAt: Date;
senderId: string;
};
export type ImageMessage = {
id: string;
body: string;
contentType: 'image';
attachments: string[];
createdAt: Date;
senderId: string;
};
export type Message = TextMessage | ImageMessage;
export type Conversation = {
id: string;
participants: Participant[];
type: string;
unreadCount: number;
messages: Message[];
};
export type SendMessage = {
conversationId: string;
messageId: string;
message: string;
contentType: 'text';
attachments: string[];
createdAt: Date | string | number;
senderId: string;
};

View File

@@ -0,0 +1,36 @@
// ----------------------------------------------------------------------
export type InvoiceAddress = {
id: string;
name: string;
address: string;
company: string;
email: string;
phone: string;
};
export type InvoiceItem = {
id: string;
title: string;
description: string;
quantity: number;
price: number;
total: number;
service: string;
};
export type Invoice = {
id: string;
sent: number;
status: string;
totalPrice: number;
invoiceNumber: string;
subTotalPrice: number;
taxes: number | string;
discount: number | string;
invoiceFrom: InvoiceAddress;
invoiceTo: InvoiceAddress;
createDate: Date | number;
dueDate: Date | number;
items: InvoiceItem[];
};

View File

@@ -0,0 +1,37 @@
export type CardComment = {
id: string;
avatar: string;
name: string;
createdAt: Date | string | number;
messageType: 'image' | 'text';
message: string;
};
export type Assignee = {
id: string;
avatar: string;
name: string;
};
export type KanbanCard = {
id: string;
name: string;
description?: string;
assignee: Assignee[];
due: [number | null, number | null];
attachments: string[];
comments: CardComment[];
completed: boolean;
};
export type KanbanColumn = {
id: string;
name: string;
cardIds: string[];
};
export type KanbanBoard = {
cards: KanbanCard[];
columns: KanbanColumn[];
columnOrder: string[];
};

View File

@@ -0,0 +1,45 @@
// ----------------------------------------------------------------------
export type MailLabelId =
| 'all'
| 'inbox'
| 'sent'
| 'drafts'
| 'trash'
| 'spam'
| 'important'
| 'starred'
| 'id_social'
| 'id_promotions'
| 'id_forums';
export type MailLabel = {
id: MailLabelId;
type: string;
name: string;
unreadCount: number;
color?: string;
};
export type Mail = {
id: string;
labelIds: string[];
folder: string | undefined;
isImportant: boolean;
isStarred: boolean;
isUnread: boolean;
subject: string;
message: string;
createdAt: Date | string | number;
files: string[];
from: {
name: string;
email: string;
avatar: null | string;
};
to: {
name: string;
email: string;
avatar: null | string;
}[];
};

View File

@@ -0,0 +1,126 @@
// ----------------------------------------------------------------------
export type PaymentType = 'paypal' | 'credit_card' | 'cash';
export type ProductStatus = 'sale' | 'new' | '';
export type ProductInventoryType = 'in_stock' | 'out_of_stock' | 'low_stock';
export type ProductCategory = 'Accessories' | 'Apparel' | 'Shoes' | string;
export type ProductGender = 'Men' | 'Women' | 'Kids' | string;
export type OnCreateBilling = (address: BillingAddress) => void;
export type ProductRating = {
name: string;
starCount: number;
reviewCount: number;
};
export type ProductReview = {
id: string;
name: string;
avatarUrl: string;
comment: string;
rating: number;
isPurchased: boolean;
helpful: number;
postedAt: Date | string | number;
};
export type Product = {
id: string;
cover: string;
images: string[];
name: string;
price: number;
code: string;
sku: string;
tags: string[];
priceSale: number | null;
totalRating: number;
totalReview: number;
ratings: ProductRating[];
reviews: ProductReview[];
colors: string[];
status: ProductStatus;
inventoryType: ProductInventoryType;
sizes: string[];
available: number;
description: string;
sold: number;
createdAt: Date | string | number;
category: ProductCategory;
gender: ProductGender;
};
export type CartItem = {
id: string;
name: string;
cover: string;
available: number;
price: number;
color: string;
size: string;
quantity: number;
subtotal: number;
};
export type BillingAddress = {
receiver: string;
phone: string;
fullAddress: string;
addressType: string;
isDefault: boolean;
};
export type ProductState = {
isLoading: boolean;
error: Error | string | null;
products: Product[];
product: Product | null;
sortBy: string | null;
filters: {
gender: string[];
category: string;
colors: string[];
priceRange: string;
rating: string;
};
checkout: {
activeStep: number;
cart: CartItem[];
subtotal: number;
total: number;
discount: number;
shipping: number;
billing: BillingAddress | null;
};
};
export type ProductFilter = {
gender: string[];
category: string;
colors: string[];
priceRange: string;
rating: string;
};
export type DeliveryOption = {
value: number;
title: string;
description: string;
};
export type PaymentOption = {
value: PaymentType;
title: string;
description: string;
icons: string[];
};
export type CardOption = {
value: string;
label: string;
};

View File

@@ -0,0 +1,128 @@
// ----------------------------------------------------------------------
export type UserInvoice = {
id: string;
createdAt: Date | string | number;
price: number;
};
export type CreditCard = {
id: string;
cardNumber: string;
cardType: string;
};
export type Follower = {
id: string;
avatarUrl: string;
name: string;
country: string;
isFollowed: boolean;
};
export type Gallery = {
id: string;
title: string;
postAt: Date | string | number;
imageUrl: string;
};
export type UserAddressBook = {
id: string;
name: string;
phone: string;
country: string;
state: string;
city: string;
street: string;
zipCode: string;
};
export type Profile = {
id: string;
cover: string;
position: string;
follower: number;
following: number;
quote: string;
country: string;
email: string;
company: string;
school: string;
role: string;
facebookLink: string;
instagramLink: string;
linkedinLink: string;
twitterLink: string;
};
export type UserManager = {
id: string;
avatarUrl: string;
name: string;
email: string;
phoneNumber: string;
address: string;
country: string;
state: string;
city: string;
zipCode: string;
company: string;
isVerified: boolean;
status: string;
role: string;
};
export type UserData = {
id: string;
avatarUrl: string;
cover: string;
name: string;
follower: number;
following: number;
totalPost: number;
position: string;
};
export type NotificationSettings = {
activityComments: boolean;
activityAnswers: boolean;
activityFollows: boolean;
applicationNews: boolean;
applicationProduct: boolean;
applicationBlog: boolean;
};
export type Friend = {
id: string;
avatarUrl: string;
name: string;
role: string;
};
export type UserPost = {
id: string;
author: {
id: string;
avatarUrl: string;
name: string;
};
isLiked: boolean;
createdAt: Date | string | number;
media: string;
message: string;
personLikes: {
name: string;
avatarUrl: string;
}[];
comments: {
id: string;
author: {
id: string;
avatarUrl: string;
name: string;
};
createdAt: Date | string | number;
message: string;
}[];
};

View File

@@ -19,7 +19,7 @@ export default function App() {
<RtlLayout>
<MotionLazyContainer>
<ProgressBarStyle />
<Settings />
{/* <Settings /> */}
<ScrollToTop />
<Router />
</MotionLazyContainer>

View File

@@ -17,36 +17,7 @@ export default function Logo({ disabledLink = false, sx }: Props) {
const logo = (
<Box sx={{ width: 40, height: 40, ...sx }}>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 512 512">
<defs>
<linearGradient id="BG1" x1="100%" x2="50%" y1="9.946%" y2="50%">
<stop offset="0%" stopColor={PRIMARY_DARK} />
<stop offset="100%" stopColor={PRIMARY_MAIN} />
</linearGradient>
<linearGradient id="BG2" x1="50%" x2="50%" y1="0%" y2="100%">
<stop offset="0%" stopColor={PRIMARY_LIGHT} />
<stop offset="100%" stopColor={PRIMARY_MAIN} />
</linearGradient>
<linearGradient id="BG3" x1="50%" x2="50%" y1="0%" y2="100%">
<stop offset="0%" stopColor={PRIMARY_LIGHT} />
<stop offset="100%" stopColor={PRIMARY_MAIN} />
</linearGradient>
</defs>
<g fill={PRIMARY_MAIN} fillRule="evenodd" stroke="none" strokeWidth="1">
<path
fill="url(#BG1)"
d="M183.168 285.573l-2.918 5.298-2.973 5.363-2.846 5.095-2.274 4.043-2.186 3.857-2.506 4.383-1.6 2.774-2.294 3.939-1.099 1.869-1.416 2.388-1.025 1.713-1.317 2.18-.95 1.558-1.514 2.447-.866 1.38-.833 1.312-.802 1.246-.77 1.18-.739 1.111-.935 1.38-.664.956-.425.6-.41.572-.59.8-.376.497-.537.69-.171.214c-10.76 13.37-22.496 23.493-36.93 29.334-30.346 14.262-68.07 14.929-97.202-2.704l72.347-124.682 2.8-1.72c49.257-29.326 73.08 1.117 94.02 40.927z"
/>
<path
fill="url(#BG2)"
d="M444.31 229.726c-46.27-80.956-94.1-157.228-149.043-45.344-7.516 14.384-12.995 42.337-25.267 42.337v-.142c-12.272 0-17.75-27.953-25.265-42.337C189.79 72.356 141.96 148.628 95.69 229.584c-3.483 6.106-6.828 11.932-9.69 16.996 106.038-67.127 97.11 135.667 184 137.278V384c86.891-1.611 77.962-204.405 184-137.28-2.86-5.062-6.206-10.888-9.69-16.994"
/>
<path
fill="url(#BG3)"
d="M450 384c26.509 0 48-21.491 48-48s-21.491-48-48-48-48 21.491-48 48 21.491 48 48 48"
/>
</g>
</svg>
<img src="/logo/logo-linksehat.png" alt="LinkSehat" />
</Box>
);

View File

@@ -14,7 +14,7 @@ interface Props extends BoxProps {
const Page = forwardRef<HTMLDivElement, Props>(({ children, title = '', meta, ...other }, ref) => (
<>
<Helmet>
<title>{`${title} | Minimal-UI`}</title>
<title>{`${title} | LinkSehat`}</title>
{meta}
</Helmet>

View File

@@ -0,0 +1,191 @@
import { ApexOptions } from 'apexcharts';
// @mui
import { useTheme } from '@mui/material/styles';
// ----------------------------------------------------------------------
export default function BaseOptionChart(): ApexOptions {
const theme = useTheme();
const LABEL_TOTAL = {
show: true,
label: 'Total',
color: theme.palette.text.secondary,
fontSize: theme.typography.subtitle2.fontSize as string,
fontWeight: theme.typography.subtitle2.fontWeight,
lineHeight: theme.typography.subtitle2.lineHeight,
};
const LABEL_VALUE = {
offsetY: 8,
color: theme.palette.text.primary,
fontSize: theme.typography.h3.fontSize as string,
fontWeight: theme.typography.h3.fontWeight,
lineHeight: theme.typography.h3.lineHeight,
};
return {
// Colors
colors: [
theme.palette.primary.main,
theme.palette.chart.yellow[0],
theme.palette.chart.blue[0],
theme.palette.chart.violet[0],
theme.palette.chart.green[0],
theme.palette.chart.red[0],
],
// Chart
chart: {
toolbar: { show: false },
zoom: { enabled: false },
// animations: { enabled: false },
foreColor: theme.palette.text.disabled,
fontFamily: theme.typography.fontFamily,
},
// States
states: {
hover: {
filter: {
type: 'lighten',
value: 0.04,
},
},
active: {
filter: {
type: 'darken',
value: 0.88,
},
},
},
// Fill
fill: {
opacity: 1,
gradient: {
type: 'vertical',
shadeIntensity: 0,
opacityFrom: 0.4,
opacityTo: 0,
stops: [0, 100],
},
},
// Datalabels
dataLabels: { enabled: false },
// Stroke
stroke: {
width: 3,
curve: 'smooth',
lineCap: 'round',
},
// Grid
grid: {
strokeDashArray: 3,
borderColor: theme.palette.divider,
},
// Xaxis
xaxis: {
axisBorder: { show: false },
axisTicks: { show: false },
},
// Markers
markers: {
size: 0,
strokeColors: theme.palette.background.paper,
},
// Tooltip
tooltip: {
x: {
show: false,
},
},
// Legend
legend: {
show: true,
fontSize: String(13),
position: 'top',
horizontalAlign: 'right',
markers: {
radius: 12,
},
fontWeight: 500,
itemMargin: { horizontal: 12 },
labels: {
colors: theme.palette.text.primary,
},
},
// plotOptions
plotOptions: {
// Bar
bar: {
columnWidth: '28%',
borderRadius: 4,
},
// Pie + Donut
pie: {
donut: {
labels: {
show: true,
value: LABEL_VALUE,
total: LABEL_TOTAL,
},
},
},
// Radialbar
radialBar: {
track: {
strokeWidth: '100%',
background: theme.palette.grey[500_16],
},
dataLabels: {
value: LABEL_VALUE,
total: LABEL_TOTAL,
},
},
// Radar
radar: {
polygons: {
fill: { colors: ['transparent'] },
strokeColors: theme.palette.divider,
connectorColors: theme.palette.divider,
},
},
// polarArea
polarArea: {
rings: {
strokeColor: theme.palette.divider,
},
spokes: {
connectorColors: theme.palette.divider,
},
},
},
// Responsive
responsive: [
{
// sm
breakpoint: theme.breakpoints.values.sm,
options: {
plotOptions: { bar: { columnWidth: '40%' } },
},
},
{
// md
breakpoint: theme.breakpoints.values.md,
options: {
plotOptions: { bar: { columnWidth: '32%' } },
},
},
],
};
}

View File

@@ -0,0 +1,58 @@
// @mui
import { alpha, useTheme } from '@mui/material/styles';
import { GlobalStyles } from '@mui/material';
// utils
import cssStyles from '../../utils/cssStyles';
// ----------------------------------------------------------------------
export default function ChartStyle() {
const theme = useTheme();
return (
<GlobalStyles
styles={{
'&.apexcharts-canvas': {
// Tooltip
'.apexcharts-xaxistooltip': {
...cssStyles(theme).bgBlur(),
border: 0,
color: theme.palette.text.primary,
boxShadow: theme.customShadows.dropdown,
borderRadius: Number(theme.shape.borderRadius) * 1.5,
'&:before': { borderBottomColor: 'transparent' },
'&:after': { borderBottomColor: alpha(theme.palette.background.default, 0.8) },
},
'.apexcharts-tooltip.apexcharts-theme-light': {
...cssStyles(theme).bgBlur(),
border: 0,
boxShadow: theme.customShadows.dropdown,
borderRadius: Number(theme.shape.borderRadius) * 1.5,
'& .apexcharts-tooltip-title': {
border: 0,
textAlign: 'center',
fontWeight: theme.typography.fontWeightBold,
backgroundColor: theme.palette.grey[500_16],
color: theme.palette.text[theme.palette.mode === 'light' ? 'secondary' : 'primary'],
},
},
// Legend
'.apexcharts-legend': {
padding: 0,
},
'.apexcharts-legend-series': {
display: 'flex !important',
alignItems: 'center',
},
'.apexcharts-legend-marker': {
marginRight: 8,
},
'.apexcharts-legend-text': {
lineHeight: '18px',
textTransform: 'capitalize',
},
},
}}
/>
);
}

View File

@@ -0,0 +1,2 @@
export { default as ChartStyle } from './ChartStyle';
export { default as BaseOptionChart } from './BaseOptionChart';

View File

@@ -30,9 +30,10 @@ export default function NavSectionVertical({
}: NavSectionProps) {
return (
<Box {...other}>
{navConfig.map((group) => (
<List key={group.subheader} disablePadding sx={{ px: 2 }}>
{navConfig.map((group, index) => (
<List key={index} disablePadding sx={{ px: 2 }}>
<ListSubheaderStyle
key={index}
sx={{
...(isCollapse && {
opacity: 0,

View File

@@ -18,7 +18,7 @@ export const HEADER = {
export const NAVBAR = {
BASE_WIDTH: 260,
DASHBOARD_WIDTH: 280,
DASHBOARD_WIDTH: 350,
DASHBOARD_COLLAPSE_WIDTH: 88,
//
DASHBOARD_ITEM_ROOT_HEIGHT: 48,

View File

@@ -2,11 +2,11 @@ import { createContext, ReactNode, useEffect, useReducer } from 'react';
// utils
import axios from '../utils/axios';
// import { isValidToken, setSession } from '../utils/jwt';
import { setSession, getSession } from '../utils/token';
import { setSession, getSession, getUser } from '../utils/token';
// @types
import { ActionMap, AuthState, AuthUser, JWTContextType } from '../@types/auth';
// ----------------------------------------------------------------------
import { Navigate, useLocation } from 'react-router-dom';
enum Types {
Initial = 'INITIALIZE',
@@ -80,25 +80,25 @@ type AuthProviderProps = {
function AuthProvider({ children }: AuthProviderProps) {
const [state, dispatch] = useReducer(JWTReducer, initialState);
let location = useLocation();
useEffect(() => {
const initialize = async () => {
try {
const accessToken = getSession();
if (accessToken) {
setSession(accessToken);
// const response = await axios.get('/api/account/my-account');
// const { user } = response.data;
// dispatch({
// type: Types.Initial,
// payload: {
// isAuthenticated: true,
// user,
// },
// });
const response = await axios.get('/user');
const user = response.data;
dispatch({
type: Types.Initial,
payload: {
isAuthenticated: true,
user,
},
});
} else {
dispatch({
type: Types.Initial,
@@ -109,7 +109,6 @@ function AuthProvider({ children }: AuthProviderProps) {
});
}
} catch (err) {
console.error(err);
dispatch({
type: Types.Initial,
payload: {
@@ -123,10 +122,7 @@ function AuthProvider({ children }: AuthProviderProps) {
initialize();
}, []);
// const csrf = () => axios.get('/sanctum/csrf-cookie')
const login = async (email: string, password: string) => {
axios
const login = async (email: string, password: string) => axios
.post('/login', { email, password })
.then((response) => {
const { user, token } = response.data;
@@ -140,9 +136,9 @@ function AuthProvider({ children }: AuthProviderProps) {
});
})
.catch(error => {
if (error.response.status !== 422) throw error
})
};
if (error.response.status !== 404) throw error.response
if (error.response.status !== 422) throw error.response
});
const register = async (email: string, password: string, firstName: string, lastName: string) => {
const response = await axios.post('/api/register', {
@@ -163,13 +159,11 @@ function AuthProvider({ children }: AuthProviderProps) {
};
const logout = async () => {
console.log('LOGOUT CALLEDS NAKJSNDKJASNDKJASDNAKJSND')
setSession(null);
dispatch({ type: Types.Logout });
};
return (
<AuthContext.Provider
return (<AuthContext.Provider
value={{
...state,
method: 'jwt',
@@ -181,6 +175,28 @@ function AuthProvider({ children }: AuthProviderProps) {
{children}
</AuthContext.Provider>
);
// if (state.isInitialized) {
// return (!state.isAuthenticated && location.pathname !== '/auth/login') ?
// (<Navigate to="/auth/login" replace={true} />)
// : false && location.pathname == '/auth/login' ?
// (<Navigate to="/dashboard" replace={true} />)
// : (
// <AuthContext.Provider
// value={{
// ...state,
// method: 'jwt',
// login,
// logout,
// register,
// }}
// >
// {children}
// </AuthContext.Provider>
// );
// } else {
// return (<Navigate to="/auth/login" replace={true} />)
// }
}
export { AuthContext, AuthProvider };

View File

@@ -26,7 +26,7 @@ export default function AuthGuard({ children }: AuthGuardProps) {
if (pathname !== requestedLocation) {
setRequestedLocation(pathname);
}
return <Login />;
return <Navigate to="/auth/login" replace={true}/>;
}
if (requestedLocation && pathname !== requestedLocation) {

View File

@@ -15,7 +15,7 @@ export default function GuestGuard({ children }: GuestGuardProps) {
const { isAuthenticated } = useAuth();
if (isAuthenticated) {
return <Navigate to={'/dashboard'} />;
return <Navigate to={'/dashboard'} replace={true}/>;
}
return <>{children}</>;

View File

@@ -6,6 +6,7 @@ import { Box, Divider, Typography, Stack, MenuItem, Avatar } from '@mui/material
import MenuPopover from '../../../components/MenuPopover';
import { IconButtonAnimate } from '../../../components/animate';
import { useNavigate } from "react-router-dom";
import useAuth from '../../../hooks/useAuth';
// ----------------------------------------------------------------------
@@ -29,6 +30,7 @@ const MENU_OPTIONS = [
export default function AccountPopover() {
const [open, setOpen] = useState<HTMLElement | null>(null);
const navigate = useNavigate();
const { logout } = useAuth();
const handleOpen = (event: React.MouseEvent<HTMLElement>) => {
setOpen(event.currentTarget);
@@ -39,6 +41,7 @@ export default function AccountPopover() {
};
const handleLogout = () => {
logout();
navigate('/auth/login');
}

View File

@@ -12,33 +12,73 @@ const ICONS = {
ecommerce: getIcon('ic_ecommerce'),
analytics: getIcon('ic_analytics'),
dashboard: getIcon('ic_dashboard'),
hospital: getIcon('ic_banking'),
};
const navConfig = [
// GENERAL
// ----------------------------------------------------------------------
{
subheader: 'general v3.2.0',
items: [
{ title: 'One', path: '/dashboard/one', icon: ICONS.dashboard },
{ title: 'Two', path: '/dashboard/two', icon: ICONS.ecommerce },
{ title: 'Three', path: '/dashboard/three', icon: ICONS.analytics },
{ title: 'Dashboard', path: '/dashboard', icon: ICONS.dashboard },
],
},
// MANAGEMENT
// Membership
// ----------------------------------------------------------------------
{
subheader: 'Management',
// subheader: 'DOCTORS & HOSPITALS',
items: [
// {
// title: 'Doctors',
// path: '/doctors',
// icon: ICONS.user,
// },
{
title: 'Master Data',
title: 'DOCTORS & HOSPITALS',
// path: '/',
icon: ICONS.user,
// icon: ICONS.user,
children: [
{ title: 'Obat', path: '/medicines' },
{ title: 'Doctors', path: '/doctors' },
{ title: 'Hospitals', path: '/hospitals' },
],
},
{
title: 'PHARMACY & DELIVERY MANAGEMENT',
children: [
{ title: 'Inventory', path: '/inventory' },
{ title: 'Delivery Services', path: '/delivery' },
],
},
{
title: 'STATION BENEFIT & MEMBERSHIP',
children: [
{ title: 'Corporate', path: '/corporates' },
{ title: 'Formularium', path: '/formularium' },
{ title: 'Diagnosis Library (ICD-X)', path: '/diagnosis' },
{ title: 'Hospitals', path: '/hospitals' },
],
},
{
title: 'CASE MANAGEMENT',
children: [
{ title: 'Request', path: '/case-request' },
],
},
{
title: 'CUSTOMER SERVICES',
children: [
{ title: 'Request', path: '/cs-request' },
],
},
{
title: 'USER MANAGEMENT',
path: '/users',
},
{
title: 'LINKING TOOLS',
path: '/linking',
},
],
},
];

View File

@@ -13,7 +13,7 @@ export default function NavbarDocs() {
>
<DocIllustration sx={{ width: 1 }} />
<div>
{/* <div>
<Typography gutterBottom variant="subtitle1">
Hi, Rayan Moran
</Typography>
@@ -23,7 +23,7 @@ export default function NavbarDocs() {
</Typography>
</div>
<Button variant="contained">Documentation</Button>
<Button variant="contained">Documentation</Button> */}
</Stack>
);
}

View File

@@ -2,7 +2,7 @@ import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
// @mui
import { styled, useTheme } from '@mui/material/styles';
import { Box, Stack, Drawer } from '@mui/material';
import { Box, Stack, Drawer, Typography } from '@mui/material';
// hooks
import useResponsive from '../../../hooks/useResponsive';
import useCollapseDrawer from '../../../hooks/useCollapseDrawer';
@@ -72,13 +72,19 @@ export default function NavbarVertical({ isOpenSidebar, onCloseSidebar }: Props)
...(isCollapse && { alignItems: 'center' }),
}}
>
<Stack direction="row" alignItems="center" justifyContent="space-between">
<Logo />
{isDesktop && !isCollapse && (
{isDesktop && !isCollapse ? (
<Stack direction="row" alignItems="center" justifyContent="space-between">
<Stack direction="row" alignItems="center">
<Logo />
<Typography ml={3}>PRIME CENTER</Typography>
</Stack>
<CollapseButton onToggleCollapse={onToggleCollapse} collapseClick={collapseClick} />
)}
</Stack>
</Stack>)
: (
<Stack direction="row" alignItems="center" justifyContent="space-between">
<Logo />
</Stack>
)}
<NavbarAccount isCollapse={isCollapse} />
</Stack>

View File

@@ -1,11 +1,13 @@
// @mui
import { Button, Container, Typography } from '@mui/material';
import { Button, Container, Grid, styled, Typography, Card, Stack } from '@mui/material';
// hooks
import useSettings from '../hooks/useSettings';
// components
import Page from '../components/Page';
import axios from '../utils/axios';
import useAuth from '../hooks/useAuth';
import SomethingUsage from '../sections/dashboard/SomethingUsage';
import { fCurrency } from '../utils/formatNumber';
// ----------------------------------------------------------------------
@@ -18,30 +20,47 @@ export default function Dashboard() {
axios.get('/user')
};
const DangerCard = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: theme.spacing(3),
color: theme.palette.error.main,
backgroundColor: theme.palette.error.lighter,
}));
const SuccessCard = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: theme.spacing(3),
color: theme.palette.success.darker,
backgroundColor: theme.palette.success.lighter,
}));
return (
<Page title="Dashboard">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Typography variant="h3" component="h1" paragraph>
Dashboard
</Typography>
<Typography gutterBottom>
Curabitur turpis. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc,
vitae euismod ligula urna in dolor. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit
id, lorem. Phasellus blandit leo ut odio. Vestibulum ante ipsum primis in faucibus orci
luctus et ultrices posuere cubilia Curae; Fusce id purus. Aliquam lorem ante, dapibus in,
viverra quis, feugiat a, tellus. In consectetuer turpis ut velit. Aenean posuere, tortor
sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus.
Vestibulum suscipit nulla quis orci. Nam commodo suscipit quam. Sed a libero.
</Typography>
<Typography>
Praesent ac sem eget est egestas volutpat. Phasellus viverra nulla ut metus varius
laoreet. Curabitur ullamcorper ultricies nisi. Ut non enim eleifend felis pretium feugiat.
Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Fusce vel dui. Quisque
libero metus, condimentum nec, tempor a, commodo mollis, magna. In enim justo, rhoncus ut,
imperdiet a, venenatis vitae, justo. Cras dapibus.
</Typography>
<Button onClick={loadSomething}>Something</Button>
<Button onClick={logout}>Logout</Button>
<Grid container spacing={2}>
<Grid item xs={6}>
<SomethingUsage />
</Grid>
<Grid item xs={6}>
<DangerCard>
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ mb: 0.6 }}>
<Typography sx={{ typography: 'subtitle2' }}>This Month Usages </Typography>
<Typography>{fCurrency(15000000)} (57)</Typography>
</Stack>
</DangerCard>
<br />
<SuccessCard>
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ mb: 0.6 }}>
<Typography sx={{ typography: 'subtitle2' }}>Remaining Balance Estimation </Typography>
<Typography>November 2022</Typography>
</Stack>
</SuccessCard>
</Grid>
</Grid>
</Container>
</Page>
);

View File

@@ -1,34 +0,0 @@
// @mui
import { Button, Container, Typography } from '@mui/material';
// hooks
import useSettings from '../../hooks/useSettings';
// components
import Page from '../../components/Page';
import axios from '../../utils/axios';
import useAuth from '../../hooks/useAuth';
import { Link } from 'react-router-dom';
// ----------------------------------------------------------------------
export default function PageOne() {
const { themeStretch } = useSettings();
const { logout } = useAuth();
const loadSomething = () => {
console.log('Loading Something')
}
return (
<Page title="Obat">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Typography variant="h3" component="h1" paragraph>
Daftar Obat
</Typography>
<Typography>askdnkasndka jsndkajsndkajsdnkajsndk jansdkasjdnkjansd</Typography>
<Button onClick={loadSomething}>Something</Button>
<Link to='/medicines/create'>asdasdasd</Link>
</Container>
</Page>
);
}

View File

@@ -0,0 +1,319 @@
// @mui
import { Box, Button, Card, Collapse, Container, FormControl, Grid, IconButton, InputLabel, MenuItem, OutlinedInput, Paper, Select, SelectChangeEvent, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, Badge } from '@mui/material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import PublishIcon from '@mui/icons-material/Publish';
// hooks
import useSettings from '../../hooks/useSettings';
// components
import Page from '../../components/Page';
import axios from '../../utils/axios';
import useAuth from '../../hooks/useAuth';
import { Link } from 'react-router-dom';
import React, { useEffect, useRef } from 'react';
import { Theme, useTheme } from '@mui/material/styles';
export default function Members() {
const { themeStretch } = useSettings();
const { logout } = useAuth();
const loadSomething = () => {
console.log('Loading Something')
}
type Member = {
id: number;
code: string;
nik: string;
name: string;
plan_code: string;
number_of_families: number;
number_of_claim: number;
active: boolean;
history: any[];
}
function createData( member: Member ): Member {
return {
...member,
history: [
{
date: '2020-01-05',
customerId: '11091700',
amount: 3,
},
{
date: '2020-01-02',
customerId: 'Anonymous',
amount: 1,
},
]
}
}
function Row(props: { row: ReturnType<typeof createData> }) {
const { row } = props;
const [open, setOpen] = React.useState(false);
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</TableCell>
<TableCell align="left">{row.code}</TableCell>
<TableCell align="left">{row.name}</TableCell>
<TableCell align="right">{row.nik}</TableCell>
<TableCell align="right">{row.plan_code}</TableCell>
<TableCell align="right">{row.number_of_claim}</TableCell>
<TableCell align="right">{row.number_of_families}</TableCell>
<TableCell align="right"><Button variant="outlined" color="success" size="small">Active</Button></TableCell>
</TableRow>
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ margin: 1 }}>
<Typography variant="h6" gutterBottom component="div">
History
</Typography>
<Table size="small" aria-label="purchases">
<TableHead>
<TableRow>
<TableCell>Date</TableCell>
<TableCell>Customer</TableCell>
<TableCell align="right">Amount</TableCell>
<TableCell align="right">Total price ($)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{row.history ? row.history.map((historyRow) => (
<TableRow key={historyRow?.date}>
<TableCell component="th" scope="row">
{historyRow?.date}
</TableCell>
<TableCell>{historyRow?.customerId}</TableCell>
<TableCell align="right">{historyRow?.amount}</TableCell>
<TableCell align="right">
{Math.round(historyRow?.amount * 1000 * 100) / 100}
</TableCell>
</TableRow>
))
: (
<TableRow>
<TableCell colSpan={8}>No Data</TableCell>
</TableRow>
)
}
</TableBody>
</Table>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
// Dummy Default Data
const [memberLoading, setMemberLoading] = React.useState(true);
const [members, setMembers] = React.useState<Member[]>([]);
const loadMembers = async () => {
setMemberLoading(true)
const response = await axios.get('/members');
setMemberLoading(false)
setMembers(response.data.map(createData));
}
useEffect(() => {
loadMembers();
}, [])
const headStyle = {
fontWeight: 'bold',
};
// FILTER SELECT
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};
const names = [
'PLAN001',
'PLAN002',
'PLAN003',
'PLAN004',
'PLAN005',
];
function getStyles(name: string, personName: string[], theme: Theme) {
return {
fontWeight:
personName.indexOf(name) === -1
? theme.typography.fontWeightRegular
: theme.typography.fontWeightMedium,
};
}
const theme = useTheme();
const [planIdFilter, setPlanIdFilter] = React.useState<string[]>([]);
const handleChangePlanID = (event: SelectChangeEvent<typeof planIdFilter>) => {
const {
target: { value },
} = event;
setPlanIdFilter(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value,
);
};
const [statusFilter, setStatusFilter] = React.useState<string[]>([]);
const handleChangeStatus = (event: SelectChangeEvent<typeof statusFilter>) => {
const {
target: { value },
} = event;
setStatusFilter(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value,
);
};
// END FILTER SELECT
// IMPORT
const importMember = React.useRef(null);
const handleImportButton = (event: any) => {
if (importMember?.current)
importMember.current.click()
else
alert('No file selected')
}
return (
<Page title="Member List">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Typography variant="h3" component="h1" paragraph>
Member List
</Typography>
<Grid container spacing={2} sx={{ mb: 2 }} justifyContent="flex-end">
<Grid item>
<TextField id="outlined-basic" label="Search" variant="outlined" sx={{ width: 400 }}/>
</Grid>
<Grid item>
<FormControl sx={{ width: 200 }}>
<InputLabel id="plan-id-label">PlanID</InputLabel>
<Select
labelId="plan-id-label"
id="plan-id"
multiple
value={planIdFilter}
onChange={handleChangePlanID}
input={<OutlinedInput label="PlanID" />}
MenuProps={MenuProps}
>
{names.map((name) => (
<MenuItem
key={name}
value={name}
style={getStyles(name, planIdFilter, theme)}
>
{name}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
<Grid item>
<FormControl sx={{ width: 200 }}>
<InputLabel id="status-filter-label">Status</InputLabel>
<Select
labelId="status-filter-label"
id="status-filter"
multiple
value={statusFilter}
onChange={handleChangeStatus}
input={<OutlinedInput label="Status" />}
MenuProps={MenuProps}
>
{['Active', 'Suspended'].map((name) => (
<MenuItem
key={name}
value={name}
style={getStyles(name, statusFilter, theme)}
>
{name}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
<Grid item>
<input id="importMember" ref={importMember} style={{ display: 'none' }} type="file" accept='.csv, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, text/plain' />
<Button variant="outlined" startIcon={<PublishIcon />} sx={{ p: 1.8 }} onClick={handleImportButton}>
Import
</Button>
</Grid>
</Grid>
<Card>
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableBody>
<TableRow>
<TableCell style={headStyle} align="left">Detail</TableCell>
<TableCell style={headStyle} align="left">MemberID</TableCell>
<TableCell style={headStyle} align="left">Name</TableCell>
<TableCell style={headStyle} align="right">NIK</TableCell>
<TableCell style={headStyle} align="right">PlanID</TableCell>
<TableCell style={headStyle} align="right">Claim&nbsp;(time)</TableCell>
<TableCell style={headStyle} align="right">Family&nbsp;(person)</TableCell>
<TableCell style={headStyle} align="right">Status</TableCell>
</TableRow>
</TableBody>
{memberLoading ?
(
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">Loading</TableCell>
</TableRow>
</TableBody>
) : (
members.length == 0 ?
(
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">No Data</TableCell>
</TableRow>
</TableBody>
) : (
<TableBody>
{members.map(row => (
<Row key={row.code} row={row} />
))}
</TableBody>
)
)}
</Table>
</TableContainer>
</Card>
</Container>
</Page>
);
}

View File

@@ -71,18 +71,21 @@ export default function Login() {
<Page title="Login">
<RootStyle>
<HeaderStyle>
<Logo />
<Logo sx={{ width: 150, height: 150 }} />
{smUp && (
<Typography variant="body2" sx={{ mt: { md: -2 } }}>
Dont have an account? {''}
<Link variant="subtitle2" component={RouterLink} to={PATH_AUTH.register}>
Get started
Has problem with your account? {''}
<Link variant="subtitle2" component={RouterLink} to="#" onClick={(e) => {
window.location.href = "mailto:admin@linksehat.com";
e.preventDefault();
}}>
Contact Us
</Link>
</Typography>
)}
</HeaderStyle>
{mdUp && (
{/* {mdUp && (
<SectionStyle>
<Typography variant="h3" sx={{ px: 5, mt: 10, mb: 5 }}>
Hi, Welcome Back
@@ -94,14 +97,14 @@ export default function Login() {
alt="login"
/>
</SectionStyle>
)}
)} */}
<Container maxWidth="sm">
<ContentStyle>
<Stack direction="row" alignItems="center" sx={{ mb: 5 }}>
<Box sx={{ flexGrow: 1 }}>
<Typography variant="h4" gutterBottom>
Sign in to Minimal
Sign in to LinkSehat
</Typography>
<Typography sx={{ color: 'text.secondary' }}>Enter your details below.</Typography>
</Box>
@@ -119,7 +122,7 @@ export default function Login() {
<LoginForm />
{!smUp && (
{false && !smUp && (
<Typography variant="body2" align="center" sx={{ mt: 3 }}>
Dont have an account?{' '}
<Link variant="subtitle2" component={RouterLink} to={PATH_AUTH.register}>

View File

@@ -11,6 +11,7 @@ import Register from '../pages/auth/Register';
import ResetPassword from '../pages/auth/ResetPassword';
import VerifyCode from '../pages/auth/VerifyCode';
import { AuthProvider } from '../contexts/LaravelAuthContext';
import AuthGuard from '../guards/AuthGuard';
// ----------------------------------------------------------------------
@@ -34,18 +35,22 @@ export default function Router() {
path: 'login',
element: (
<AuthProvider>
<Login />
<GuestGuard>
<Login />
</GuestGuard>
</AuthProvider>
),
},
{
path: 'register',
element: (
<AuthProvider>
<GuestGuard>
<RegisterForm />
</GuestGuard>
</AuthProvider>
),
},
// {
// path: 'register',
// element: (
// <GuestGuard>
// <RegisterForm />
// </GuestGuard>
// ),
// },
// { path: 'login-unprotected', element: <Login /> },
// { path: 'register-unprotected', element: <Register /> },
// { path: 'reset-password', element: <ResetPassword /> },
@@ -58,15 +63,21 @@ export default function Router() {
// },
{
path: '/',
element: <DashboardLayout />,
element: (
<AuthProvider>
<AuthGuard>
<DashboardLayout />
</AuthGuard>
</AuthProvider>),
children:[
{ element: <Navigate to="/dashboard" replace />, index: true },
{
path: 'medicines',
element: <AuthProvider><Medicines /></AuthProvider>,
path: 'dashboard',
element: <Dashboard />,
},
{
path: 'medicines/create',
element: <MedicinesCreate />
path: 'members',
element: <Members />,
},
]
},
@@ -109,6 +120,6 @@ const Login = Loadable(lazy(() => import('../pages/auth/Login')));
const Dashboard = Loadable(lazy(() => import('../pages/Dashboard')));
const NotFound = Loadable(lazy(() => import('../pages/Page404')));
// Medicines
const Medicines = Loadable(lazy(() => import('../pages/Medicines/Index')));
// Members
const Members = Loadable(lazy(() => import('../pages/Members/Index')));
const MedicinesCreate = Loadable(lazy(() => import('../pages/Medicines/Create')));

View File

@@ -39,8 +39,8 @@ export default function LoginForm() {
});
const defaultValues = {
email: '',
password: '',
email: 'admin@linksehat.dev',
password: 'password',
remember: true,
};
@@ -58,15 +58,16 @@ export default function LoginForm() {
const onSubmit = async (data: FormValuesProps) => {
try {
await login(data.email, data.password );
navigate('/dashboard/one');
const loginResult = await login(data.email, data.password );
navigate('/dashboard');
} catch (error) {
console.error(error);
reset();
if (isMountedRef.current) {
setError('afterSubmit', { ...error, message: error.message });
setError('afterSubmit', { ...error, message: error.data.message });
}
}
};
@@ -74,6 +75,7 @@ export default function LoginForm() {
return (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Stack spacing={3}>
<Alert severity='info'>Email : admin@linksehat.dev & Password : password</Alert>
{!!errors.afterSubmit && <Alert severity="error">{errors.afterSubmit.message}</Alert>}
<RHFTextField name="email" label="Email address" />

View File

@@ -0,0 +1,80 @@
import merge from 'lodash/merge';
import ReactApexChart from 'react-apexcharts';
// @mui
import { styled } from '@mui/material/styles';
import { Card, Typography, Stack } from '@mui/material';
// utils
import { fCurrency, fPercent } from '../../utils/formatNumber';
// components
import Iconify from '../../components/Iconify';
import BaseOptionChart from '../../components/chart/BaseOptionChart';
// ----------------------------------------------------------------------
const RootStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: theme.spacing(3),
color: theme.palette.primary.darker,
backgroundColor: theme.palette.primary.lighter,
}));
// ----------------------------------------------------------------------
const INITIAL = 500000000
const TOTAL = 257907000;
const PERCENT = -3;
const CHART_DATA = [{ data: [100, 99, 99, 85, 74, 57, 54, 51] }];
export default function SomethingUsage() {
const chartOptions = merge(BaseOptionChart(), {
chart: { sparkline: { enabled: true } },
xaxis: { labels: { show: true } },
yaxis: { labels: { show: false } },
stroke: { width: 4 },
legend: { show: false },
grid: { show: false },
tooltip: {
marker: { show: false },
y: {
formatter: (seriesName: string) => (seriesName) + "%",
title: {
formatter: () => '',
},
},
},
fill: { gradient: { opacityFrom: 0, opacityTo: 0 } },
});
return (
<RootStyle>
<Stack direction="row" justifyContent="space-between" sx={{ mb: 3 }}>
<div>
<Typography variant="body2" component="span" sx={{ opacity: 0.72 }}>
{fCurrency(INITIAL)}
</Typography>
<Typography sx={{ typography: 'subtitle2' }}>Remaining Balance</Typography>
<Typography sx={{ typography: 'h3' }}>{fCurrency(TOTAL)}</Typography>
</div>
<div>
<Stack direction="row" alignItems="center" justifyContent="flex-end" sx={{ mb: 0.6 }}>
<Iconify
width={20}
height={20}
icon={PERCENT >= 0 ? 'eva:trending-up-fill' : 'eva:trending-down-fill'}
/>
<Typography variant="subtitle2" component="span" sx={{ ml: 0.5 }}>
{PERCENT > 0 && '+'}
{fPercent(PERCENT)}
</Typography>
</Stack>
<Typography variant="body2" component="span" sx={{ opacity: 0.72 }}>
&nbsp;than last month
</Typography>
</div>
</Stack>
<ReactApexChart type="area" series={CHART_DATA} options={chartOptions} height={100} />
</RootStyle>
);
}

View File

@@ -21,7 +21,7 @@ const axiosInstance = axios.create({
axiosInstance.interceptors.response.use(
(response) => response,
(error) => Promise.reject((error.response && error.response.data) || 'Something went wrong')
(error) => Promise.reject((error) || 'Something went wrong')
);
export default axiosInstance;

View File

@@ -1,9 +1,31 @@
import numeral from 'numeral';
// ----------------------------------------------------------------------
// load a locale
numeral.register('locale', 'id', {
delimiters: {
thousands: '.',
decimal: ','
},
abbreviations: {
thousand: 'k',
million: 'm',
billion: 'b',
trillion: 't'
},
ordinal : function (number: number) {
return number === 1 ? 'er' : 'ème';
},
currency: {
symbol: 'Rp '
}
});
// switch between locales
numeral.locale('id');
export function fCurrency(number: string | number) {
return numeral(number).format(Number.isInteger(number) ? '$0,0' : '$0,0.00');
return numeral(number).format('$0,0');
}
export function fPercent(number: number) {

View File

@@ -38,6 +38,15 @@ const setSession = (accessToken: string | null) => {
}
};
const getSession = () => window.localStorage.getItem('accessToken')
const setUser = (user: any) => {
if (user) {
localStorage.setItem('user', user);
} else {
localStorage.removeItem('user');
}
};
export { setSession, getSession };
const getSession = () => window.localStorage.getItem('accessToken')
const getUser = () => window.localStorage.getItem('user')
export { setSession, getSession, setUser, getUser };