Base Template
@@ -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"
|
||||||
|
|||||||
3
frontend/dashboard/.env.production
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
GENERATE_SOURCEMAP=false
|
||||||
|
|
||||||
|
VITE_API_URL="https://aso-api.linksehat.dev/api"
|
||||||
@@ -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>
|
||||||
22
frontend/dashboard/.pnpm-debug.log
Normal 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?"
|
||||||
|
}
|
||||||
@@ -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",
|
||||||
|
|||||||
2111
frontend/dashboard/pnpm-lock.yaml
generated
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 543 B After Width: | Height: | Size: 573 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
BIN
frontend/dashboard/public/logo/logo-linksehat.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
66
frontend/dashboard/src/@types/auth.ts
Normal 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;
|
||||||
|
};
|
||||||
55
frontend/dashboard/src/@types/blog.ts
Normal 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[];
|
||||||
|
};
|
||||||
14
frontend/dashboard/src/@types/calendar.ts
Normal 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 };
|
||||||
|
};
|
||||||
65
frontend/dashboard/src/@types/chat.ts
Normal 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;
|
||||||
|
};
|
||||||
36
frontend/dashboard/src/@types/invoice.ts
Normal 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[];
|
||||||
|
};
|
||||||
37
frontend/dashboard/src/@types/kanban.ts
Normal 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[];
|
||||||
|
};
|
||||||
45
frontend/dashboard/src/@types/mail.ts
Normal 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;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
126
frontend/dashboard/src/@types/product.ts
Normal 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;
|
||||||
|
};
|
||||||
128
frontend/dashboard/src/@types/user.ts
Normal 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;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
@@ -19,7 +19,7 @@ export default function App() {
|
|||||||
<RtlLayout>
|
<RtlLayout>
|
||||||
<MotionLazyContainer>
|
<MotionLazyContainer>
|
||||||
<ProgressBarStyle />
|
<ProgressBarStyle />
|
||||||
<Settings />
|
{/* <Settings /> */}
|
||||||
<ScrollToTop />
|
<ScrollToTop />
|
||||||
<Router />
|
<Router />
|
||||||
</MotionLazyContainer>
|
</MotionLazyContainer>
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
191
frontend/dashboard/src/components/chart/BaseOptionChart.tsx
Normal 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%' } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
58
frontend/dashboard/src/components/chart/ChartStyle.tsx
Normal 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',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
2
frontend/dashboard/src/components/chart/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export { default as ChartStyle } from './ChartStyle';
|
||||||
|
export { default as BaseOptionChart } from './BaseOptionChart';
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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}</>;
|
||||||
|
|||||||
@@ -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');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
319
frontend/dashboard/src/pages/Members/Index.tsx
Normal 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 (time)</TableCell>
|
||||||
|
<TableCell style={headStyle} align="right">Family (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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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 } }}>
|
||||||
Don’t 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 }}>
|
||||||
Don’t have an account?{' '}
|
Don’t have an account?{' '}
|
||||||
<Link variant="subtitle2" component={RouterLink} to={PATH_AUTH.register}>
|
<Link variant="subtitle2" component={RouterLink} to={PATH_AUTH.register}>
|
||||||
|
|||||||
@@ -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')));
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
80
frontend/dashboard/src/sections/dashboard/SomethingUsage.tsx
Normal 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 }}>
|
||||||
|
than last month
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<ReactApexChart type="area" series={CHART_DATA} options={chartOptions} height={100} />
|
||||||
|
</RootStyle>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||