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 PORT=8083
REACT_APP_HOST_API_URL="http://localhost:8000" 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> <IfModule mod_rewrite.c>
RewriteEngine On RewriteEngine On
RewriteBase / 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": "eslint --ext .ts,.tsx ./src",
"lint:fix": "eslint --fix --ext .ts,.tsx ./src", "lint:fix": "eslint --fix --ext .ts,.tsx ./src",
"start": "vite", "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", "serve": "vite preview",
"clear-all": "rm -rf build node_modules", "clear-all": "rm -rf build node_modules",
"re-start": "rm -rf build node_modules && yarn install && yarn start", "re-start": "rm -rf build node_modules && yarn install && yarn start",
@@ -44,6 +44,7 @@
"@iconify/react": "^3.2.1", "@iconify/react": "^3.2.1",
"@mui/lab": "5.0.0-alpha.80", "@mui/lab": "5.0.0-alpha.80",
"@mui/material": "^5.6.4", "@mui/material": "^5.6.4",
"@mui/icons-material": "^5.8.0",
"@mui/system": "^5.6.4", "@mui/system": "^5.6.4",
"@mui/x-data-grid": "^5.10.0", "@mui/x-data-grid": "^5.10.0",
"@vitejs/plugin-react": "^1.3.2", "@vitejs/plugin-react": "^1.3.2",
@@ -52,6 +53,7 @@
"date-fns": "^2.28.0", "date-fns": "^2.28.0",
"framer-motion": "^6.3.3", "framer-motion": "^6.3.3",
"history": "^5.3.0", "history": "^5.3.0",
"jsx-runtime": "^1.2.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"notistack": "^2.0.4", "notistack": "^2.0.4",
"nprogress": "^0.2.0", "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> <RtlLayout>
<MotionLazyContainer> <MotionLazyContainer>
<ProgressBarStyle /> <ProgressBarStyle />
<Settings /> {/* <Settings /> */}
<ScrollToTop /> <ScrollToTop />
<Router /> <Router />
</MotionLazyContainer> </MotionLazyContainer>

View File

@@ -17,36 +17,7 @@ export default function Logo({ disabledLink = false, sx }: Props) {
const logo = ( const logo = (
<Box sx={{ width: 40, height: 40, ...sx }}> <Box sx={{ width: 40, height: 40, ...sx }}>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 512 512"> <img src="/logo/logo-linksehat.png" alt="LinkSehat" />
<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>
</Box> </Box>
); );

View File

@@ -14,7 +14,7 @@ interface Props extends BoxProps {
const Page = forwardRef<HTMLDivElement, Props>(({ children, title = '', meta, ...other }, ref) => ( const Page = forwardRef<HTMLDivElement, Props>(({ children, title = '', meta, ...other }, ref) => (
<> <>
<Helmet> <Helmet>
<title>{`${title} | Minimal-UI`}</title> <title>{`${title} | LinkSehat`}</title>
{meta} {meta}
</Helmet> </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) { }: NavSectionProps) {
return ( return (
<Box {...other}> <Box {...other}>
{navConfig.map((group) => ( {navConfig.map((group, index) => (
<List key={group.subheader} disablePadding sx={{ px: 2 }}> <List key={index} disablePadding sx={{ px: 2 }}>
<ListSubheaderStyle <ListSubheaderStyle
key={index}
sx={{ sx={{
...(isCollapse && { ...(isCollapse && {
opacity: 0, opacity: 0,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,33 +12,73 @@ const ICONS = {
ecommerce: getIcon('ic_ecommerce'), ecommerce: getIcon('ic_ecommerce'),
analytics: getIcon('ic_analytics'), analytics: getIcon('ic_analytics'),
dashboard: getIcon('ic_dashboard'), dashboard: getIcon('ic_dashboard'),
hospital: getIcon('ic_banking'),
}; };
const navConfig = [ const navConfig = [
// GENERAL // GENERAL
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
{ {
subheader: 'general v3.2.0',
items: [ items: [
{ title: 'One', path: '/dashboard/one', icon: ICONS.dashboard }, { title: 'Dashboard', path: '/dashboard', icon: ICONS.dashboard },
{ title: 'Two', path: '/dashboard/two', icon: ICONS.ecommerce },
{ title: 'Three', path: '/dashboard/three', icon: ICONS.analytics },
], ],
}, },
// MANAGEMENT // Membership
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
{ {
subheader: 'Management', // subheader: 'DOCTORS & HOSPITALS',
items: [ items: [
// {
// title: 'Doctors',
// path: '/doctors',
// icon: ICONS.user,
// },
{ {
title: 'Master Data', title: 'DOCTORS & HOSPITALS',
// path: '/', // path: '/',
icon: ICONS.user, // icon: ICONS.user,
children: [ 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 }} /> <DocIllustration sx={{ width: 1 }} />
<div> {/* <div>
<Typography gutterBottom variant="subtitle1"> <Typography gutterBottom variant="subtitle1">
Hi, Rayan Moran Hi, Rayan Moran
</Typography> </Typography>
@@ -23,7 +23,7 @@ export default function NavbarDocs() {
</Typography> </Typography>
</div> </div>
<Button variant="contained">Documentation</Button> <Button variant="contained">Documentation</Button> */}
</Stack> </Stack>
); );
} }

View File

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

View File

@@ -1,11 +1,13 @@
// @mui // @mui
import { Button, Container, Typography } from '@mui/material'; import { Button, Container, Grid, styled, Typography, Card, Stack } from '@mui/material';
// hooks // hooks
import useSettings from '../hooks/useSettings'; import useSettings from '../hooks/useSettings';
// components // components
import Page from '../components/Page'; import Page from '../components/Page';
import axios from '../utils/axios'; import axios from '../utils/axios';
import useAuth from '../hooks/useAuth'; 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') 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 ( return (
<Page title="Dashboard"> <Page title="Dashboard">
<Container maxWidth={themeStretch ? false : 'xl'}> <Container maxWidth={themeStretch ? false : 'xl'}>
<Typography variant="h3" component="h1" paragraph> <Typography variant="h3" component="h1" paragraph>
Dashboard Dashboard
</Typography> </Typography>
<Typography gutterBottom>
Curabitur turpis. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi congue nunc, <Grid container spacing={2}>
vitae euismod ligula urna in dolor. Nam quam nunc, blandit vel, luctus pulvinar, hendrerit <Grid item xs={6}>
id, lorem. Phasellus blandit leo ut odio. Vestibulum ante ipsum primis in faucibus orci <SomethingUsage />
luctus et ultrices posuere cubilia Curae; Fusce id purus. Aliquam lorem ante, dapibus in, </Grid>
viverra quis, feugiat a, tellus. In consectetuer turpis ut velit. Aenean posuere, tortor <Grid item xs={6}>
sed cursus feugiat, nunc augue blandit nunc, eu sollicitudin urna dolor sagittis lacus. <DangerCard>
Vestibulum suscipit nulla quis orci. Nam commodo suscipit quam. Sed a libero. <Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ mb: 0.6 }}>
</Typography> <Typography sx={{ typography: 'subtitle2' }}>This Month Usages </Typography>
<Typography> <Typography>{fCurrency(15000000)} (57)</Typography>
Praesent ac sem eget est egestas volutpat. Phasellus viverra nulla ut metus varius </Stack>
laoreet. Curabitur ullamcorper ultricies nisi. Ut non enim eleifend felis pretium feugiat. </DangerCard>
Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Fusce vel dui. Quisque <br />
libero metus, condimentum nec, tempor a, commodo mollis, magna. In enim justo, rhoncus ut, <SuccessCard>
imperdiet a, venenatis vitae, justo. Cras dapibus. <Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ mb: 0.6 }}>
</Typography> <Typography sx={{ typography: 'subtitle2' }}>Remaining Balance Estimation </Typography>
<Button onClick={loadSomething}>Something</Button> <Typography>November 2022</Typography>
<Button onClick={logout}>Logout</Button> </Stack>
</SuccessCard>
</Grid>
</Grid>
</Container> </Container>
</Page> </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"> <Page title="Login">
<RootStyle> <RootStyle>
<HeaderStyle> <HeaderStyle>
<Logo /> <Logo sx={{ width: 150, height: 150 }} />
{smUp && ( {smUp && (
<Typography variant="body2" sx={{ mt: { md: -2 } }}> <Typography variant="body2" sx={{ mt: { md: -2 } }}>
Dont have an account? {''} Has problem with your account? {''}
<Link variant="subtitle2" component={RouterLink} to={PATH_AUTH.register}> <Link variant="subtitle2" component={RouterLink} to="#" onClick={(e) => {
Get started window.location.href = "mailto:admin@linksehat.com";
e.preventDefault();
}}>
Contact Us
</Link> </Link>
</Typography> </Typography>
)} )}
</HeaderStyle> </HeaderStyle>
{mdUp && ( {/* {mdUp && (
<SectionStyle> <SectionStyle>
<Typography variant="h3" sx={{ px: 5, mt: 10, mb: 5 }}> <Typography variant="h3" sx={{ px: 5, mt: 10, mb: 5 }}>
Hi, Welcome Back Hi, Welcome Back
@@ -94,14 +97,14 @@ export default function Login() {
alt="login" alt="login"
/> />
</SectionStyle> </SectionStyle>
)} )} */}
<Container maxWidth="sm"> <Container maxWidth="sm">
<ContentStyle> <ContentStyle>
<Stack direction="row" alignItems="center" sx={{ mb: 5 }}> <Stack direction="row" alignItems="center" sx={{ mb: 5 }}>
<Box sx={{ flexGrow: 1 }}> <Box sx={{ flexGrow: 1 }}>
<Typography variant="h4" gutterBottom> <Typography variant="h4" gutterBottom>
Sign in to Minimal Sign in to LinkSehat
</Typography> </Typography>
<Typography sx={{ color: 'text.secondary' }}>Enter your details below.</Typography> <Typography sx={{ color: 'text.secondary' }}>Enter your details below.</Typography>
</Box> </Box>
@@ -119,7 +122,7 @@ export default function Login() {
<LoginForm /> <LoginForm />
{!smUp && ( {false && !smUp && (
<Typography variant="body2" align="center" sx={{ mt: 3 }}> <Typography variant="body2" align="center" sx={{ mt: 3 }}>
Dont have an account?{' '} Dont have an account?{' '}
<Link variant="subtitle2" component={RouterLink} to={PATH_AUTH.register}> <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 ResetPassword from '../pages/auth/ResetPassword';
import VerifyCode from '../pages/auth/VerifyCode'; import VerifyCode from '../pages/auth/VerifyCode';
import { AuthProvider } from '../contexts/LaravelAuthContext'; import { AuthProvider } from '../contexts/LaravelAuthContext';
import AuthGuard from '../guards/AuthGuard';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@@ -34,18 +35,22 @@ export default function Router() {
path: 'login', path: 'login',
element: ( element: (
<AuthProvider> <AuthProvider>
<Login /> <GuestGuard>
<Login />
</GuestGuard>
</AuthProvider>
),
},
{
path: 'register',
element: (
<AuthProvider>
<GuestGuard>
<RegisterForm />
</GuestGuard>
</AuthProvider> </AuthProvider>
), ),
}, },
// {
// path: 'register',
// element: (
// <GuestGuard>
// <RegisterForm />
// </GuestGuard>
// ),
// },
// { path: 'login-unprotected', element: <Login /> }, // { path: 'login-unprotected', element: <Login /> },
// { path: 'register-unprotected', element: <Register /> }, // { path: 'register-unprotected', element: <Register /> },
// { path: 'reset-password', element: <ResetPassword /> }, // { path: 'reset-password', element: <ResetPassword /> },
@@ -58,15 +63,21 @@ export default function Router() {
// }, // },
{ {
path: '/', path: '/',
element: <DashboardLayout />, element: (
<AuthProvider>
<AuthGuard>
<DashboardLayout />
</AuthGuard>
</AuthProvider>),
children:[ children:[
{ element: <Navigate to="/dashboard" replace />, index: true },
{ {
path: 'medicines', path: 'dashboard',
element: <AuthProvider><Medicines /></AuthProvider>, element: <Dashboard />,
}, },
{ {
path: 'medicines/create', path: 'members',
element: <MedicinesCreate /> element: <Members />,
}, },
] ]
}, },
@@ -109,6 +120,6 @@ const Login = Loadable(lazy(() => import('../pages/auth/Login')));
const Dashboard = Loadable(lazy(() => import('../pages/Dashboard'))); const Dashboard = Loadable(lazy(() => import('../pages/Dashboard')));
const NotFound = Loadable(lazy(() => import('../pages/Page404'))); const NotFound = Loadable(lazy(() => import('../pages/Page404')));
// Medicines // Members
const Medicines = Loadable(lazy(() => import('../pages/Medicines/Index'))); const Members = Loadable(lazy(() => import('../pages/Members/Index')));
const MedicinesCreate = Loadable(lazy(() => import('../pages/Medicines/Create'))); const MedicinesCreate = Loadable(lazy(() => import('../pages/Medicines/Create')));

View File

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

View File

@@ -1,9 +1,31 @@
import numeral from 'numeral'; 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) { 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) { 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 };