This commit is contained in:
louiscklaw
2025-01-31 20:14:02 +08:00
parent 49e275d85d
commit 5c584709c4
706 changed files with 40207 additions and 0 deletions

5
tsc1877/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
.env
**/volumes
**/*.zip
**/*.7z
**/draft*

BIN
tsc1877/ConEmu64_jwEAbC5dbe.png (Stored with Git LFS) Normal file

Binary file not shown.

7
tsc1877/gitUpdate.bat Normal file
View File

@@ -0,0 +1,7 @@
git status .
@pause
git add .
git commit -m"update tsc1877,"
start git push

20
tsc1877/meta.md Normal file
View File

@@ -0,0 +1,20 @@
---
tags: [aws, react, nextjs, javascript]
---
# tsc1877
## Balance history
### quotation
- phase 2 HKD 400
- phase 3 HKD 400
- phase 4 HKD 400
- phase 5 HKD 400
- login page HKD 200 (variation order)
### payment history
HKD 200 deposit paid
HKD 800 phase2 & 3 paid

View File

@@ -0,0 +1,18 @@
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
[*.yml]
[*.{yml,yaml}]
indent_size = 2

View File

@@ -0,0 +1,17 @@
module.exports = {
arrowParens: 'avoid',
bracketSpacing: true,
htmlWhitespaceSensitivity: 'css',
insertPragma: false,
bracketSameLine: false,
jsxSingleQuote: true,
printWidth: 120,
proseWrap: 'preserve',
quoteProps: 'as-needed',
requirePragma: false,
semi: false,
singleQuote: true,
tabWidth: 2,
trailingComma: 'none',
useTabs: false
}

327
tsc1877/task1/NOTES.md Normal file
View File

@@ -0,0 +1,327 @@
1. 你想我做? (做完比番你 / $)
2. 你想我做完話你知點做? (花時間 / $$)
3. 你想我經你隻手做? (花花花時間 / $$$)
有冇咩取向??
我唔係想同你打心理戰,但係我想知一知呢樣野先
10 個識等如全部識晒,咁你會比幾多個識比你自己?
我諗我會報 HKD 400 / phase
你用 mac ?
留一個聯絡方式?
ttps://t.me/louislabs/
留一留個 ec2 個登入方法?
thanks
Q: 我俾 pem key 同 elastic ip 你?
A: 可以呀,你經果頁 upload 比我 OK ?
揸定主意,用 nextjs 啦嘛
© 2024, Made with ❤️ by ThemeSelection
會唔會可以再簡單小小例如唔洗右上嗰啲function?
### references
https://themeselection.com/item/materio-free-mui-nextjs-admin-template/
---
51.21.113.230
---
## Phase 2
### ASSIGNMENT MARKING CHECKLIST V1
#### PHASE 2A: SECURE SERVER SETUP (DEADLINE FEB.17, 2024) (SUBTOTAL: 5')
In this phase, you are required to set up a secure server for later development. Some guidance will be given in the tutorial.
1. Instantiate a free Virtual Cloud Machine (Amazon EC2 recommended or other free VPS) **\_** / 1'
- [ ] Details of the Free Usage Tier: http://aws.amazon.com/free
- [ ] You can apply for the education coupon on AWS
- [ ] With a Linux distribution, install only Ngnix, NodeJS and SQLite (or MySQL)
- [ ] To minimize the attack surfaces, always install only what you need
1. Apply necessary security configurations **\_** / 2'
- [ ] Apply proper firewall settings to your VM: block all ports except 22, 80 and 443 only
- [ ] Apply proper updates for the server software packages in a regular manner
- [ ] Hide the versions of OS, Ngnix, and NodeJS in HTTP response headers
1. Configure the VM so that your website is accessible at http://sxx.ierg4210.ie.cuhk.edu.hk **\_** / 2'
- [ ] Apply for an elastic public IP, and ALWAYS associate it with the instantiated VM
- [ ] Submit your elastic IP through the provided Google Form link1 before 5pm, Feb 17, 2024
- [ ] TAs will then assign you a domain name and configure the DNS mapping for you
- [ ] Upload all your pages to the server. They should then be accessible through:
- [ ] http://[your-own-public-IP], or
- [ ] http://sxx.ierg4210.ie.cuhk.edu.hk
#### PHASE 2B: DATA PRESENTATION & MANAGEMENT (DEADLINE FEB. 25, 2024) (SUBTOTAL: 20')
In this phase, you will implement the core functions of the website mainly with NodeJS and SQL.
1. SQL: Create a database with the following structures (to be covered in the tutorial) **\_** / 1'
- [x] A table for `categories`
- [x] Required columns:
-[ ] [x] `cid` (primary key),
-[ ] [x] `name`
- [x] Data: at least 3 `categories` of your choice
- [x] A table for `products`
- [x] Required columns:
-[ ] [x] `pid (primary key)`,
-[ ] [x] `cid`,
-[ ] [x] `name`,
-[ ] [x] `price`,
-[ ] [x] `description`
- [x] Data: at least 3 products for each category
1. HTML, NodeJS & SQL: Create an `admin panel`
- [x] Design several HTML forms to manage(you can `insert`, `update` and `delete` information, `CRUD`) products in DB **\_** / 5'
-[ ] [x] `insert`
-[ ] [x] `update`
-[ ] [x] `delete`
-[ ] [x] Dropdown menu to select `cid` according to its name
-[ ] [x] Input fields for inputting
-[ ] [x] `name`,
-[ ] [x] `price`
-[ ] [x] Textarea for inputting `description`
- [x] Design several HTML forms to manage `categories` in DB **\_** / 2'
-[ ] [x] `insert`
-[ ] [x] `update`
-[ ] [x] `delete`
2. HTML, NodeJS, SQL: Update the ***main page*** created in Phase 1
- [x] Populate the `category` list from DB **\_** / 1'
- [x] Based on the `category` selected by the user, populate the corresponding product list from DB **\_** / 3'
-[ ] [x] The `cid=[x]` is reflected as a query string in the URL
-[ ] [x] 你想用邊種? 新做法我可以留番個註解比你 -> 新
3. HTML, NodeJS & SQL: Update the `product details` page created in Phase 1 **\_** / 2'
- [x] Display the details of a product based on its DB record
- [?] Display the inventory of a product based on its DB record
4. Support of automatic image resizing for product images **\_** / 3'
5. Supporting HTML5 Drag-and-drop file selection in the admin panel **\_** / 2'
- [x] limit to upload jpg file
- [x] File field for uploading an image (format: jpg/gif/png, size: <=5MB)
- [/] For the file uploaded, store it with its name. **\_** / 1'
-[ ] default to `my_path/image.jpg`
- [?] When a large image is uploaded, the server will resize it and show a thumbnail image in the panel
- [x] On the main page, display thumbnails. On the product description page, display the larger image
- [?] Create a dropping area that takes an image
- [?] Display a thumbnail (i.e. smaller width and height) if the dropped file is an image; reject it otherwise
### !!!CSRF
---
## PHASE 3
### AJAX SHOPPING LIST (DEADLINE: MAR 10, 2024) (SUBTOTAL: 13')
In this phase, you will implement the shopping list, which allows users to shop around your products. This phase is designed to let you practise Javascript programming.
1. JS: Dynamically update# the `shopping list`
- [x] When the `addToCart` button of a product is clicked, add it to the shopping list **\_** / 1'
-[ ] [x] Adding the same product twice will display only one row of record
- Once a product is added,
-[ ] Users are allowed to
-[ ] [x] update its `quantity` and
-[ ] [x] delete it with a number input, or
-[ ] [x] two buttons for increment and decrement
-[ ] [x] Store its
-[ ] [x] `pid` and
-[ ] [x] `quantity` in the browser's `localStorage` **\_** / 2'
-[ ] [x] Get the
-[ ] [x] `name` and
-[ ] [x] `price` from the server (with `pid` as input) **\_** / 3'
-[ ] [x] Calculate and display the `total amount` at the client-side **\_** / 1'
- [x] Once the page is reloaded, the shopping list is restored **\_** / 2'
-[ ] Page reloads when
-[ ] [x] users browse another category or
-[ ] [x] visit the product detail page
-[ ] [x] Populate and retrieve the stored products from the `localStorage`
- [?] Supporting infinite scroll when browsing products in the main page **\_** / 3'
<sub>#The whole process of shopping list management must be done without a page load</sub>
---
- aws setup swap file
- https://repost.aws/knowledge-center/ec2-memory-swap-file
---
## phase 5
### Paypal Sandbox Accounts
1. Sign up at https://developer.paypal.com/dashboard/ : ______ / 1'
a. Create two sandbox accounts a merchant account and a buyer account:
i. A merchant account Email and Password
sb-j4nbw29912003@business.example.com
X<LqO7Sb
ii. A buyer account Email and Password
sb-b00gb30304504@personal.example.com
4s@0f>IE
b. Create a sandbox application linked to the merchant account:
i. an application Client ID
https://developer.paypal.com/dashboard/accounts/edit/4829534511909691981?accountName=sb-j4nbw29912003@business.example.com
helloworld
Client ID
AQT5-eAKNK7IhAhBGlbHBu_9jBx74ZLCfEioKUWQMXMuMmLcnffmpoUz_z-ewOuKZmpSlDk74UtlH58O
Secret key 1
EMtwOzHACol7Tx8NHOa3eV3TPu2AerZEunJ3MBZfYYcxPeUDy-TNLqqcdL0UDQsrHMpQKeCRPcjKnwNy
#### Paypal Integration - Front End
Ref:
- https://developer.paypal.com/community/blog/how-to-add-paypal-checkout-payments-to-your-react-app/
2. Create a checkout button via PayPal standard checkout APIs: ______ / 1'
a [x] Include the PayPal JavaScript SDK
b [x] Set up a container element for the button
c [x] Render the button by paypal.Buttons().render()
3. [x] When the checkout button is clicked, `createOrder()` is called: ______ / 3'
a [x] every individual product (or any other data) to server.
-[x] `name`
-[x] `quantity`
b [x] It waits for server to generate a JSON string named `orderDetails`.
-[ ] refer backend `6.`
c [x] It submits the order now to PayPal using the actions.order.create() function.
4 [x] After the buyer has completed the payment, `onApprove()` is called: ______ / 2'
a [x] It passes the `orderDetails` to server.
b [x] It clears the shopping cart.
5 [x] If the buyer cancels the payments, `onCancel()` is called: ______ / 1'
a [x] It passes necessary information to server.
#### Paypal Integration - Back End
6. Server follows the following steps to generate the `orderDetails`: ______ / 5'
a. server generates a digest that is the hash from a string composed of at least:
-[x] of each selected product
-[x] `name`
-[x] `quantity`
-[x] The `price` of each selected product gathered from DB,
-[x] The `total price` of all selected products,
-[x] `Currency code`,
-[x] Merchants `email address`, and
-[x] A `random salt`
b.[x] server puts the generated digest in the `custom_id` of an order.
c.[x] server generates an `UUID`, and puts it in the `invoice_id` of an order.
d.[x] server generates other necessary fields of an order, including at least:
-[x] `amount` - the `total price` and `currency code`
-[x] `items` - the `name`, `quantity`, `price` of each selected item
7. Server follows the following steps to insert and update the DB orders table: ______ / 3'
a. When `createOrder()` is called, it inserts a new entry:
-[x] `UUID` - TEXT (Primary Key)
- [x] custom_id
- [x] invoice_id
-[x] `username` - TEXT `customer's username`
-[x] `digest` - TEXT, `custom_id`
-[x] `salt` - TEXT
b. [x] When `onApprove()` is called, it updates the corresponding entry:
-[] `orderDetails` - TEXT
c. [x] When `onCancel()` is called, it deletes the corresponding entry.
#### Miscellaneous Changes
8. [x] Display the DB orders table in the admin panel: product list, payment status…etc. ______ / 2'
9. [x] Let members check the most recent five orders in the member portal . ______ / 2'
### signal flow
#### Notes version:
```mermaid
sequenceDiagram
actor Client
Note left of Client: checkout button is clicked
Note left of Client: `createOrder() is called
Client->>Server: fetch("/my-server/get-order-details"...
Note over Server: generate `orderDetails`
Server->>Client: return json `orderDetails`
Note over Client: purchase_units: [amount, itesm, custom_id, invoice_id]
Paypal-->>Client: send payment page
Note over Client: Client proceed payment
Client-->>Paypal: client approve payment
Note over Client: When `onApprove()` is called
Client->>Server: fetch("/my-server/save-order"...
Note over Server: it `update` the corresponding entry
Note over Client: When `onCancel()` is called
Client->>Server: fetch("/my-server/cancel-order"...
Note over Server: it `deletes` the corresponding entry
```
#### My version:
```mermaid
sequenceDiagram
actor Client
Note left of Client: checkout button is clicked
Note left of Client: createOrder() is called
Client->>Server: fetch("/my-server/get-order-details"...
Note over Server: generate `orderDetails`
Server->>Client: return json `orderDetails`
Note over Client: purchase_units: [amount, itesm, custom_id, invoice_id]
Paypal-->>Client: send payment page
Note over Client: Client proceed payment
Client-->>Paypal: client approve payment
Paypal->>Server: event_type: CHECKOUT.ORDER.APPROVED
Note left of Paypal: with (resources.id + resources.status)
Note over Client: When `onApprove()` is called
Client->>Server: fetch("/my-server/save-order"...
Note right of Client: with resources.id
Note over Server: receive 2 id, found match and stastus "approved"
Note over Server: it `update` the corresponding entry
Note over Client: When `onCancel()` is called
Client->>Server: fetch("/my-server/cancel-order"...
Note over Server: it `deletes` the corresponding entry
```

View File

@@ -0,0 +1 @@
13.50.75.106

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpgIBAAKCAQEA60pfYaPm/QVZAq2VRMJRP+YmvHseni8TlvB22Cy5c2lY5Npq
Pt432WzmgS2I0SeGUGZbMSf2G6rvrSQP+s9FaQxkqS2JrZI43StO0zu2ogTOD605
1XZvQSnVljodTLJrLcYmNa7h4Fj2Avr1//Lsm83w8WQlycnz4u772MsiR7L57miX
vSJfaVyDeEEZRb0Ovd3qeFmGfgcPIyZPoCof7BP0uK2gywcHYuOcEn1K7yEl/4ja
mXmJL5WWjchvNfOXFLzcQitDhtd6gGcnO5Zxdyby1r2GtQLMXErN5Imsb+V0iwhh
lYT2WzSiOF6YpZudG/aCplLWutgINsnJek0FUwIDAQABAoIBAQCt4kPFE+zUFoym
dwP2UajcEUmVR2PkQSQ4CzO5JKxVB4jDcErOdUlO7LUYyD7xW0Vey5YfKMovvk7s
ZNdOYrmIHbmScW3rEUHm6WeSofGhoO1sFnfEMoeb37/SCta2ZLDiniqEx95z/yqo
HzGH2s5TgonKy1lF3HLZxgXnPI/z7XzZrhVOq7LCechLToB9isJtJDH0kr/av2f0
VRdbwfREgQ7OsmehaP4sCPHeRwe4w8v03a7YUYLIRxPrTKiLW/Fw9RUOnTS13bj5
HirWIHHXAVXFdI1dHwZ0ViUITkk/twZe48WUaHcC9zdWqNoq+Ln5bQN5MuFO0A30
DBrhIpCpAoGBAP6WTM0pqk7n+IvOpWBmFatWq6Oe9Vdl5ua4AKInDAduDK5T9Vvq
+dWk+IKbvVi5/faO5g2c7MIq4oXDgu1ngawuMsH/HBs08SmRUrDVTuDJrUEh8BGe
h+Qnsx+WV4kBNWiT89QiX7Z/2UMbF73HQ00IOXqZHA9OFR8iwaVcGjrtAoGBAOyY
qESmRSEw+hzmRHvJgXKzR7LxJuOnn9mp6KVVgaf1VZ4FMOroui9yn6W0973ROFL4
z+ZB2STcZwO7iY+HI5MfS0WfK77x3MQNDICm8rGQBdV2/Y1vbM1IoJMedEOnWTKe
nRK7WkHMuZuaoVtXsDE9Y7BIQLqtb0g4LhkFqPk/AoGBAI2aQ7WJizTASbpZghqQ
LVVTq7wiWgv2neuORKvsVAB98Ye1Iy+xDq0KST88KKEFMoDFEqhXtEh7WetUx6oM
fE+skobcwmFPnSOltPyuaGUXR8qGeeUS7Dq26iaTlLSbe31jbad8RwhXLBzrINVp
08Ew5eeO6dCJea4LwvGavOwxAoGBAI2t2M3LN4hpuMzHg2kRQOg7vYrFaAFZppqE
aaFgM9UtT8jh2OpC1QOGw11KojRT7kOZZX3AHtU4+qM8kWKWjVITolKdv2eHlCj+
m2cLEjgMQ8IRkEI64E+J10ZiLUM6Gmr6mprWAHV4sAkjLs7xErxZw7GTgteG9iWs
JsarIgWnAoGBALSsZD5Ib4vIKp40AHe/Nu0+ZXzFzqFbxmW5oVDvTE3kp0Nvij5a
GPJAqvqyF0LizCwqEO08gyY13/NpsVJ5J+TMdP6eGIIT5ewGGspslrTeuxAq20Wx
zGn6ytrECrdtYVZkPpKnXWzsx4NooywTNhTKCVjyvOpH+/7GtdPcH9/c
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,42 @@
const { Sequelize } = require('sequelize')
const sequelize = new Sequelize('app_db', 'db_user', 'db_user_pass', {
host: 'localhost',
port: 6033,
dialect: 'mysql'
})
const User = sequelize.define(
'User',
{
firstName: {
type: Sequelize.STRING,
allowNull: false
},
lastName: {
type: Sequelize.STRING,
allowNull: false
}
},
{ timestamps: false }
)
;(async () => {
try {
await sequelize.authenticate()
console.log('Connection has been established successfully.')
// create table
await sequelize.sync()
const user = await User.create({ firstName: 'John', lastName: 'Doe' })
console.log('Created user: ', user)
const users = await User.findAll()
console.log('Found all users: ', users)
await sequelize.close()
} catch (error) {
console.error('Unable to connect to the database:', error)
}
})()

View File

@@ -0,0 +1,17 @@
{
"name": "sequelize",
"version": "1.0.0",
"description": "",
"main": "helloworld.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"mysql2": "^3.9.3",
"sequelize": "^6.37.1",
"sqlite3": "^5.1.7"
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
tsc1877/task1/_ref/IERG4210-24S-Phase1/0.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
tsc1877/task1/_ref/IERG4210-24S-Phase1/1.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
tsc1877/task1/_ref/IERG4210-24S-Phase1/2.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
tsc1877/task1/_ref/IERG4210-24S-Phase1/3.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
tsc1877/task1/_ref/chrome_XeuGI0DOuj.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,78 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>IERG4210 Admin Panel</title>
<style>
#drop-area {
width: 300px;
height: 200px;
border: 2px dashed #ccc;
border-radius: 5px;
text-align: center;
font-family: Arial, sans-serif;
margin: 0 auto;
margin-top: 20px;
}
#drop-area.highlight {
background-color: #f1f1f1;
}
#drop-area .message {
margin: 80px auto;
font-size: 18px;
color: #888;
}
#thumbnail {
max-width: 200px;
max-height: 200px;
margin-top: 10px;
}
</style>
</head>
<body>
<h1>IERG4210 Admin Panel</h1>
<h1>Add Category</h1>
<form action="/addCategory" method="POST">
<label for="categoryName">Category Name:</label>
<input type="text" id="categoryName" name="categoryName" required>
<button type="submit">Add Category</button>
</form>
<h1>Add Product</h1>
<form action="/addProduct" method="POST">
<label for="productName">Product Name:</label>
<input type="text" id="productName" name="productName" required>
<label for="category">Category:</label>
<select id="category" name="category" required>
<option value="" selected disabled>Select a category</option>
<script>
// Fetch categories from the server and populate the dropdown options
fetch('/categories')
.then(response => response.json())
.then(categories => {
const categorySelect = document.getElementById('category');
categories.forEach(category => {
const option = document.createElement('option');
option.value = category.catid;
option.textContent = category.name;
categorySelect.appendChild(option);
});
})
.catch(error => console.error(error));
</script>
</select>
<label for="price">Price:</label>
<input type="number" id="price" name="price" required>
<label for="description">Description:</label>
<textarea id="description" name="description" required></textarea>
<label for="image">Image:</label>
<input type="file" id="image" name="image" accept="image/*" required>
<button type="submit">Add Product</button>
</form>
</body>
</html>

View File

@@ -0,0 +1,85 @@
// Import the 'path' module
const path = require("path");
const express = require("express");
const sqlite3 = require("sqlite3").verbose();
const app = express();
const db = new sqlite3.Database("cart.db");
// Use middleware to parse the request body
app.use(express.urlencoded({ extended: true }));
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "form.html"));
});
app.get("/categories", (req, res) => {
const selectCategoriesQuery = `SELECT * FROM categories`;
db.all(selectCategoriesQuery, (err, rows) => {
if (err) {
console.error(err);
res.status(500).json({ error: "Failed to fetch categories" });
} else {
res.json(rows);
}
});
});
// Handle POST request to add a new category
app.post("/addCategory", (req, res) => {
const categoryName = req.body.categoryName;
// Insert the new category into the categories table
const insertCategoryQuery = `INSERT INTO categories (name) VALUES (?)`;
db.run(insertCategoryQuery, [categoryName], (err) => {
if (err) {
console.error(err);
res.status(500).send("Error adding category");
} else {
res.send(`
<script>
alert('Category added successfully');
window.location.href = '/';
</script>
`);
}
});
});
// Handle POST request to add a new product
app.post("/addProduct", (req, res) => {
const productName = req.body.productName;
const category = req.body.category;
const price = req.body.price;
const description = req.body.description;
let imageUrl = "";
if (req.file) {
imageUrl = req.file.filename;
}
// Insert the new product into the products table
const insertProductQuery = `INSERT INTO products (name, catid, price, description) VALUES (?, ?, ?, ?)`;
db.run(
insertProductQuery,
[productName, category, price, description],
(err) => {
if (err) {
console.error(err);
res.status(500).send("Error adding product");
} else {
res.send(`
<script>
alert('Category added successfully');
window.location.href = '/';
</script>
`);
}
}
);
});
// Start the server
app.listen(3000, () => {
console.log("Server started on port 3000");
});

BIN
tsc1877/task1/_ref/paymentsequence_Simple.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,5 @@
DB_HOST=localhost
DB_PORT=6033
DB_NAME=app_db
DB_USER=db_user
DB_PASSWORD=db_user_pass

View File

@@ -0,0 +1,12 @@
```batch
> .\dc_up.bat
```
*nix
```batch
> .\dc_up.sh
```

View File

@@ -0,0 +1,51 @@
// ** MUI Imports
import Box from '@mui/material/Box';
import Card from '@mui/material/Card';
import Avatar from '@mui/material/Avatar';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import CardContent from '@mui/material/CardContent';
// ** Icons Imports
import DotsVertical from 'mdi-material-ui/DotsVertical';
const CardStatsVertical = props => {
// ** Props
const { title, subtitle, color, icon, stats, trend, trendNumber } = props;
return (
<Card>
<CardContent>
<Box sx={{ display: 'flex', marginBottom: 5.5, alignItems: 'flex-start', justifyContent: 'space-between' }}>
<Avatar sx={{ boxShadow: 3, marginRight: 4, color: 'common.white', backgroundColor: `${color}.main` }}>
{icon}
</Avatar>
<IconButton size="small" aria-label="settings" className="card-more-options" sx={{ color: 'text.secondary' }}>
<DotsVertical />
</IconButton>
</Box>
<Typography sx={{ fontWeight: 600, fontSize: '0.875rem' }}>{title}</Typography>
<Box sx={{ marginTop: 1.5, display: 'flex', flexWrap: 'wrap', marginBottom: 1.5, alignItems: 'flex-start' }}>
<Typography variant="h6" sx={{ mr: 2 }}>
{stats}
</Typography>
<Typography
component="sup"
variant="caption"
sx={{ color: trend === 'positive' ? 'success.main' : 'error.main' }}
>
{trendNumber}
</Typography>
</Box>
<Typography variant="caption">{subtitle}</Typography>
</CardContent>
</Card>
);
};
export default CardStatsVertical;
CardStatsVertical.defaultProps = {
color: 'primary',
trend: 'positive',
};

View File

@@ -0,0 +1,7 @@
// ** Next Import
import dynamic from 'next/dynamic';
// ! To avoid 'Window is not defined' error
const ReactApexcharts = dynamic(() => import('react-apexcharts'), { ssr: false });
export default ReactApexcharts;

View File

@@ -0,0 +1,39 @@
// ** MUI Imports
import Zoom from '@mui/material/Zoom';
import { styled } from '@mui/material/styles';
import useScrollTrigger from '@mui/material/useScrollTrigger';
const ScrollToTopStyled = styled('div')(({ theme }) => ({
zIndex: 11,
position: 'fixed',
right: theme.spacing(6),
bottom: theme.spacing(10),
}));
const ScrollToTop = props => {
// ** Props
const { children, className } = props;
// ** init trigger
const trigger = useScrollTrigger({
threshold: 400,
disableHysteresis: true,
});
const handleClick = () => {
const anchor = document.querySelector('body');
if (anchor) {
anchor.scrollIntoView({ behavior: 'smooth' });
}
};
return (
<Zoom in={trigger}>
<ScrollToTopStyled className={className} onClick={handleClick} role="presentation">
{children}
</ScrollToTopStyled>
</Zoom>
);
};
export default ScrollToTop;

View File

@@ -0,0 +1,30 @@
// ** React Imports
import { createContext, useState } from 'react';
// ** ThemeConfig Import
import themeConfig from 'src/configs/themeConfig';
const initialSettings = {
themeColor: 'primary',
mode: themeConfig.mode,
contentWidth: themeConfig.contentWidth,
};
// ** Create Context
export const SettingsContext = createContext({
saveSettings: () => null,
settings: initialSettings,
});
export const SettingsProvider = ({ children }) => {
// ** State
const [settings, setSettings] = useState({ ...initialSettings });
const saveSettings = updatedSettings => {
setSettings(updatedSettings);
};
return <SettingsContext.Provider value={{ settings, saveSettings }}>{children}</SettingsContext.Provider>;
};
export const SettingsConsumer = SettingsContext.Consumer;

View File

@@ -0,0 +1,4 @@
import { useContext } from 'react';
import { SettingsContext } from 'src/@core/context/settingsContext';
export const useSettings = () => useContext(SettingsContext);

View File

@@ -0,0 +1,37 @@
// ** MUI Imports
import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
// Styled component for Blank Layout component
const BlankLayoutWrapper = styled(Box)(({ theme }) => ({
height: '100vh',
// For V1 Blank layout pages
'& .content-center': {
display: 'flex',
minHeight: '100vh',
alignItems: 'center',
justifyContent: 'center',
padding: theme.spacing(5),
},
// For V2 Blank layout pages
'& .content-right': {
display: 'flex',
minHeight: '100vh',
overflowX: 'hidden',
position: 'relative',
},
}));
const BlankLayout = ({ children }) => {
return (
<BlankLayoutWrapper className="layout-wrapper">
<Box className="app-content" sx={{ minHeight: '100vh', overflowX: 'hidden', position: 'relative' }}>
{children}
</Box>
</BlankLayoutWrapper>
);
};
export default BlankLayout;

View File

@@ -0,0 +1,104 @@
// ** MUI Imports
import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
import { Badge, Button, IconButton, Stack, Typography } from '@mui/material';
import { useRouter } from 'next/router';
import HomeIcon from '@mui/icons-material/Home';
import { CartConsumer, CartContext, CartProvider } from 'src/contexts/cart';
import { useContext, useEffect, useState } from 'react';
import CartIcon from './components/CartIcon';
import AccountBoxIcon from '@mui/icons-material/AccountBox';
import CheckSession from 'src/api/checkSession';
import { AuthContext, AuthProvider } from 'src/contexts/auth';
// Styled component for Blank Layout component
const ShopfrontLayoutWrapper = styled(Box)(({ theme }) => ({
height: '100vh',
// For V1 Blank layout pages
'& .content-center': {
display: 'flex',
minHeight: '100vh',
alignItems: 'center',
justifyContent: 'center',
padding: theme.spacing(5),
},
// For V2 Blank layout pages
'& .content-right': {
display: 'flex',
minHeight: '100vh',
overflowX: 'hidden',
position: 'relative',
},
}));
const ShopfrontLayout = ({ children }) => {
const router = useRouter();
const [data, setData] = useState({});
const { username, setUsername } = useContext(AuthContext);
useEffect(() => {
const check = async () => {
try {
let { session } = JSON.parse(localStorage.getItem('session')) || '';
let data = await CheckSession({ session });
if (data['status'] != 'OK') {
return route.replace('/shopfront/login');
} else {
setData(data);
setUsername(data['username']);
}
} catch (error) {}
};
return check();
}, []);
return (
<CartProvider>
<ShopfrontLayoutWrapper className="layout-wrapper">
<Box className="app-content" sx={{ minHeight: '100vh', overflowX: 'hidden', position: 'relative' }}>
<Stack direction="column" alignItems={'center'} justifyContent={'center'}>
<Box sx={{ width: '80vw' }}>
<Stack direction="column">
<Stack direction="row" justifyContent={'space-between'} alignItems={'center'} minHeight={'10vh'}>
<Box style={{ width: 'calc ( 80vw / 3 )' }}>
<Typography variant="h5">Hi, {username || 'Guest'}</Typography>
</Box>
<Box style={{ width: 'calc ( 80vw / 3 )' }}>
<Typography variant="h5">VTKH Mall</Typography>
</Box>
<Box style={{ width: 'calc ( 80vw / 3 )' }}>
<IconButton
onClick={() => {
router.push('/shopfront/customer/profile');
}}
>
<AccountBoxIcon />
</IconButton>
<IconButton
onClick={() => {
router.push('/shopfront');
}}
>
<HomeIcon />
</IconButton>
<CartIcon />
</Box>
</Stack>
{children}
<Box height={'5rem'}></Box>
</Stack>
</Box>
</Stack>
</Box>
</ShopfrontLayoutWrapper>
</CartProvider>
);
};
export default ShopfrontLayout;

View File

@@ -0,0 +1,109 @@
// ** React Imports
import { useState } from 'react';
// ** MUI Imports
import Fab from '@mui/material/Fab';
import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
// ** Icons Imports
import ArrowUp from 'mdi-material-ui/ArrowUp';
// ** Theme Config Import
import themeConfig from 'src/configs/themeConfig';
// ** Components
import AppBar from './components/vertical/appBar';
import Navigation from './components/vertical/navigation';
import Footer from './components/shared-components/footer';
import ScrollToTop from 'src/@core/components/scroll-to-top';
// ** Styled Component
import DatePickerWrapper from 'src/@core/styles/libs/react-datepicker';
const VerticalLayoutWrapper = styled('div')({
height: '100%',
display: 'flex',
});
const MainContentWrapper = styled(Box)({
flexGrow: 1,
minWidth: 0,
display: 'flex',
minHeight: '100vh',
flexDirection: 'column',
});
const ContentWrapper = styled('main')(({ theme }) => ({
flexGrow: 1,
width: '100%',
padding: theme.spacing(6),
transition: 'padding .25s ease-in-out',
[theme.breakpoints.down('sm')]: {
paddingLeft: theme.spacing(4),
paddingRight: theme.spacing(4),
},
}));
const VerticalLayout = props => {
// ** Props
const { settings, children, scrollToTop } = props;
// ** Vars
const { contentWidth } = settings;
const navWidth = themeConfig.navigationSize;
// ** States
const [navVisible, setNavVisible] = useState(false);
// ** Toggle Functions
const toggleNavVisibility = () => setNavVisible(!navVisible);
return (
<>
<VerticalLayoutWrapper className="layout-wrapper">
<Navigation
navWidth={navWidth}
navVisible={navVisible}
setNavVisible={setNavVisible}
toggleNavVisibility={toggleNavVisibility}
{...props}
/>
<MainContentWrapper className="layout-content-wrapper">
<AppBar toggleNavVisibility={toggleNavVisibility} {...props} />
<ContentWrapper
className="layout-page-content"
sx={{
...(contentWidth === 'boxed' && {
mx: 'auto',
'@media (min-width:1440px)': { maxWidth: 1440 },
'@media (min-width:1200px)': { maxWidth: '100%' },
}),
}}
>
{children}
</ContentWrapper>
<Footer {...props} />
<DatePickerWrapper sx={{ zIndex: 11 }}>
<Box id="react-datepicker-portal"></Box>
</DatePickerWrapper>
</MainContentWrapper>
</VerticalLayoutWrapper>
{scrollToTop ? (
scrollToTop(props)
) : (
<ScrollToTop className="mui-fixed">
<Fab color="primary" size="small" aria-label="scroll back to top">
<ArrowUp />
</Fab>
</ScrollToTop>
)}
</>
);
};
export default VerticalLayout;

View File

@@ -0,0 +1,29 @@
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
import { Badge, IconButton } from '@mui/material';
import { useRouter } from 'next/router';
import { useContext } from 'react';
import { CartContext } from 'src/contexts/cart';
function CartIcon() {
let router = useRouter();
let { cart } = useContext(CartContext);
if (!cart) return <></>;
return (
<>
<IconButton
onClick={() => {
router.push('/shopfront/cart');
}}
>
<Badge badgeContent={cart?.length} color="primary">
<ShoppingCartIcon />
</Badge>
</IconButton>
</>
);
}
export default CartIcon;

View File

@@ -0,0 +1,30 @@
import IconButton from '@mui/material/IconButton';
// ** Icons Imports
import WeatherNight from 'mdi-material-ui/WeatherNight';
import WeatherSunny from 'mdi-material-ui/WeatherSunny';
const ModeToggler = props => {
// ** Props
const { settings, saveSettings } = props;
const handleModeChange = mode => {
saveSettings({ ...settings, mode });
};
const handleModeToggle = () => {
if (settings.mode === 'light') {
handleModeChange('dark');
} else {
handleModeChange('light');
}
};
return (
<IconButton color="inherit" aria-haspopup="true" onClick={handleModeToggle}>
{settings.mode === 'dark' ? <WeatherSunny /> : <WeatherNight />}
</IconButton>
);
};
export default ModeToggler;

View File

@@ -0,0 +1,217 @@
// ** React Imports
import { useState, Fragment } from 'react';
// ** MUI Imports
import Box from '@mui/material/Box';
import Chip from '@mui/material/Chip';
import Button from '@mui/material/Button';
import IconButton from '@mui/material/IconButton';
import { styled } from '@mui/material/styles';
import useMediaQuery from '@mui/material/useMediaQuery';
import MuiMenu from '@mui/material/Menu';
import MuiAvatar from '@mui/material/Avatar';
import MuiMenuItem from '@mui/material/MenuItem';
import Typography from '@mui/material/Typography';
// ** Icons Imports
import BellOutline from 'mdi-material-ui/BellOutline';
// ** Third Party Components
import PerfectScrollbarComponent from 'react-perfect-scrollbar';
// ** Styled Menu component
const Menu = styled(MuiMenu)(({ theme }) => ({
'& .MuiMenu-paper': {
width: 380,
overflow: 'hidden',
marginTop: theme.spacing(4),
[theme.breakpoints.down('sm')]: {
width: '100%',
},
},
'& .MuiMenu-list': {
padding: 0,
},
}));
// ** Styled MenuItem component
const MenuItem = styled(MuiMenuItem)(({ theme }) => ({
paddingTop: theme.spacing(3),
paddingBottom: theme.spacing(3),
borderBottom: `1px solid ${theme.palette.divider}`,
}));
const styles = {
maxHeight: 349,
'& .MuiMenuItem-root:last-of-type': {
border: 0,
},
};
// ** Styled PerfectScrollbar component
const PerfectScrollbar = styled(PerfectScrollbarComponent)({
...styles,
});
// ** Styled Avatar component
const Avatar = styled(MuiAvatar)({
width: '2.375rem',
height: '2.375rem',
fontSize: '1.125rem',
});
// ** Styled component for the title in MenuItems
const MenuItemTitle = styled(Typography)(({ theme }) => ({
fontWeight: 600,
flex: '1 1 100%',
overflow: 'hidden',
fontSize: '0.875rem',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
marginBottom: theme.spacing(0.75),
}));
// ** Styled component for the subtitle in MenuItems
const MenuItemSubtitle = styled(Typography)({
flex: '1 1 100%',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
});
const NotificationDropdown = () => {
// ** States
const [anchorEl, setAnchorEl] = useState(null);
// ** Hook
const hidden = useMediaQuery(theme => theme.breakpoints.down('lg'));
const handleDropdownOpen = event => {
setAnchorEl(event.currentTarget);
};
const handleDropdownClose = () => {
setAnchorEl(null);
};
const ScrollWrapper = ({ children }) => {
if (hidden) {
return <Box sx={{ ...styles, overflowY: 'auto', overflowX: 'hidden' }}>{children}</Box>;
} else {
return (
<PerfectScrollbar options={{ wheelPropagation: false, suppressScrollX: true }}>{children}</PerfectScrollbar>
);
}
};
return (
<Fragment>
<IconButton color="inherit" aria-haspopup="true" onClick={handleDropdownOpen} aria-controls="customized-menu">
<BellOutline />
</IconButton>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={handleDropdownClose}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
>
<MenuItem disableRipple>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}>
<Typography sx={{ fontWeight: 600 }}>Notifications</Typography>
<Chip
size="small"
label="8 New"
color="primary"
sx={{ height: 20, fontSize: '0.75rem', fontWeight: 500, borderRadius: '10px' }}
/>
</Box>
</MenuItem>
<ScrollWrapper>
<MenuItem onClick={handleDropdownClose}>
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center' }}>
<Avatar alt="Flora" src="/images/avatars/4.png" />
<Box sx={{ mx: 4, flex: '1 1', display: 'flex', overflow: 'hidden', flexDirection: 'column' }}>
<MenuItemTitle>Congratulation Flora! 🎉</MenuItemTitle>
<MenuItemSubtitle variant="body2">Won the monthly best seller badge</MenuItemSubtitle>
</Box>
<Typography variant="caption" sx={{ color: 'text.disabled' }}>
Today
</Typography>
</Box>
</MenuItem>
<MenuItem onClick={handleDropdownClose}>
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center' }}>
<Avatar sx={{ color: 'common.white', backgroundColor: 'primary.main' }}>VU</Avatar>
<Box sx={{ mx: 4, flex: '1 1', display: 'flex', overflow: 'hidden', flexDirection: 'column' }}>
<MenuItemTitle>New user registered.</MenuItemTitle>
<MenuItemSubtitle variant="body2">5 hours ago</MenuItemSubtitle>
</Box>
<Typography variant="caption" sx={{ color: 'text.disabled' }}>
Yesterday
</Typography>
</Box>
</MenuItem>
<MenuItem onClick={handleDropdownClose}>
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center' }}>
<Avatar alt="message" src="/images/avatars/5.png" />
<Box sx={{ mx: 4, flex: '1 1', display: 'flex', overflow: 'hidden', flexDirection: 'column' }}>
<MenuItemTitle>New message received 👋🏻</MenuItemTitle>
<MenuItemSubtitle variant="body2">You have 10 unread messages</MenuItemSubtitle>
</Box>
<Typography variant="caption" sx={{ color: 'text.disabled' }}>
11 Aug
</Typography>
</Box>
</MenuItem>
<MenuItem onClick={handleDropdownClose}>
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center' }}>
<img width={38} height={38} alt="paypal" src="/images/misc/paypal.png" />
<Box sx={{ mx: 4, flex: '1 1', display: 'flex', overflow: 'hidden', flexDirection: 'column' }}>
<MenuItemTitle>Paypal</MenuItemTitle>
<MenuItemSubtitle variant="body2">Received Payment</MenuItemSubtitle>
</Box>
<Typography variant="caption" sx={{ color: 'text.disabled' }}>
25 May
</Typography>
</Box>
</MenuItem>
<MenuItem onClick={handleDropdownClose}>
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center' }}>
<Avatar alt="order" src="/images/avatars/3.png" />
<Box sx={{ mx: 4, flex: '1 1', display: 'flex', overflow: 'hidden', flexDirection: 'column' }}>
<MenuItemTitle>Revised Order 📦</MenuItemTitle>
<MenuItemSubtitle variant="body2">New order revised from john</MenuItemSubtitle>
</Box>
<Typography variant="caption" sx={{ color: 'text.disabled' }}>
19 Mar
</Typography>
</Box>
</MenuItem>
<MenuItem onClick={handleDropdownClose}>
<Box sx={{ width: '100%', display: 'flex', alignItems: 'center' }}>
<img width={38} height={38} alt="chart" src="/images/misc/chart.png" />
<Box sx={{ mx: 4, flex: '1 1', display: 'flex', overflow: 'hidden', flexDirection: 'column' }}>
<MenuItemTitle>Finance report has been generated</MenuItemTitle>
<MenuItemSubtitle variant="body2">25 hrs ago</MenuItemSubtitle>
</Box>
<Typography variant="caption" sx={{ color: 'text.disabled' }}>
27 Dec
</Typography>
</Box>
</MenuItem>
</ScrollWrapper>
<MenuItem
disableRipple
sx={{ py: 3.5, borderBottom: 0, borderTop: theme => `1px solid ${theme.palette.divider}` }}
>
<Button fullWidth variant="contained" onClick={handleDropdownClose}>
Read All Notifications
</Button>
</MenuItem>
</Menu>
</Fragment>
);
};
export default NotificationDropdown;

View File

@@ -0,0 +1,143 @@
// ** React Imports
import { useState, Fragment } from 'react';
// ** Next Import
import { useRouter } from 'next/router';
// ** MUI Imports
import Box from '@mui/material/Box';
import Menu from '@mui/material/Menu';
import Badge from '@mui/material/Badge';
import Avatar from '@mui/material/Avatar';
import Divider from '@mui/material/Divider';
import MenuItem from '@mui/material/MenuItem';
import { styled } from '@mui/material/styles';
import Typography from '@mui/material/Typography';
// ** Icons Imports
import CogOutline from 'mdi-material-ui/CogOutline';
import CurrencyUsd from 'mdi-material-ui/CurrencyUsd';
import EmailOutline from 'mdi-material-ui/EmailOutline';
import LogoutVariant from 'mdi-material-ui/LogoutVariant';
import AccountOutline from 'mdi-material-ui/AccountOutline';
import MessageOutline from 'mdi-material-ui/MessageOutline';
import HelpCircleOutline from 'mdi-material-ui/HelpCircleOutline';
// ** Styled Components
const BadgeContentSpan = styled('span')(({ theme }) => ({
width: 8,
height: 8,
borderRadius: '50%',
backgroundColor: theme.palette.success.main,
boxShadow: `0 0 0 2px ${theme.palette.background.paper}`,
}));
const UserDropdown = () => {
// ** States
const [anchorEl, setAnchorEl] = useState(null);
// ** Hooks
const router = useRouter();
const handleDropdownOpen = event => {
setAnchorEl(event.currentTarget);
};
const handleDropdownClose = url => {
if (url) {
router.push(url);
}
setAnchorEl(null);
};
const styles = {
py: 2,
px: 4,
width: '100%',
display: 'flex',
alignItems: 'center',
color: 'text.primary',
textDecoration: 'none',
'& svg': {
fontSize: '1.375rem',
color: 'text.secondary',
},
};
return (
<Fragment>
<Badge>Hello user,</Badge>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={() => handleDropdownClose()}
sx={{ '& .MuiMenu-paper': { width: 230, marginTop: 4 } }}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
>
<Box sx={{ pt: 2, pb: 3, px: 4 }}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Badge
overlap="circular"
badgeContent={<BadgeContentSpan />}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
>
<Avatar alt="John Doe" src="/images/avatars/1.png" sx={{ width: '2.5rem', height: '2.5rem' }} />
</Badge>
<Box sx={{ display: 'flex', marginLeft: 3, alignItems: 'flex-start', flexDirection: 'column' }}>
<Typography sx={{ fontWeight: 600 }}>John Doe</Typography>
<Typography variant="body2" sx={{ fontSize: '0.8rem', color: 'text.disabled' }}>
Admin
</Typography>
</Box>
</Box>
</Box>
<Divider sx={{ mt: 0, mb: 1 }} />
<MenuItem sx={{ p: 0 }} onClick={() => handleDropdownClose()}>
<Box sx={styles}>
<AccountOutline sx={{ marginRight: 2 }} />
Profile
</Box>
</MenuItem>
<MenuItem sx={{ p: 0 }} onClick={() => handleDropdownClose()}>
<Box sx={styles}>
<EmailOutline sx={{ marginRight: 2 }} />
Inbox
</Box>
</MenuItem>
<MenuItem sx={{ p: 0 }} onClick={() => handleDropdownClose()}>
<Box sx={styles}>
<MessageOutline sx={{ marginRight: 2 }} />
Chat
</Box>
</MenuItem>
<Divider />
<MenuItem sx={{ p: 0 }} onClick={() => handleDropdownClose()}>
<Box sx={styles}>
<CogOutline sx={{ marginRight: 2 }} />
Settings
</Box>
</MenuItem>
<MenuItem sx={{ p: 0 }} onClick={() => handleDropdownClose()}>
<Box sx={styles}>
<CurrencyUsd sx={{ marginRight: 2 }} />
Pricing
</Box>
</MenuItem>
<MenuItem sx={{ p: 0 }} onClick={() => handleDropdownClose()}>
<Box sx={styles}>
<HelpCircleOutline sx={{ marginRight: 2 }} />
FAQ
</Box>
</MenuItem>
<Divider />
<MenuItem sx={{ py: 2 }} onClick={() => handleDropdownClose('/pages/login')}>
<LogoutVariant sx={{ marginRight: 2, fontSize: '1.375rem', color: 'text.secondary' }} />
Logout
</MenuItem>
</Menu>
</Fragment>
);
};
export default UserDropdown;

View File

@@ -0,0 +1,53 @@
// ** MUI Imports
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import Typography from '@mui/material/Typography';
import useMediaQuery from '@mui/material/useMediaQuery';
const FooterContent = () => {
// ** Var
const hidden = useMediaQuery(theme => theme.breakpoints.down('md'));
return (
<Box sx={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', justifyContent: 'space-between' }}>
<Typography sx={{ mr: 2 }}>
{`© ${new Date().getFullYear()}, Made with `}
<Box component="span" sx={{ color: 'error.main' }}>
</Box>
{` by `}
<Link target="_blank" href="https://themeselection.com/">
ThemeSelection
</Link>{' '}
and for project
</Typography>
{hidden ? null : (
<Box sx={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', '& :not(:last-child)': { mr: 4 } }}>
<Link
target="_blank"
href="https://github.com/themeselection/materio-mui-react-nextjs-admin-template-free/blob/main/LICENSE"
>
MIT License
</Link>
<Link target="_blank" href="https://themeselection.com/">
More Themes
</Link>
<Link
target="_blank"
href="https://github.com/themeselection/materio-mui-react-nextjs-admin-template-free/blob/main/README.md"
>
Documentation
</Link>
<Link
target="_blank"
href="https://github.com/themeselection/materio-mui-react-nextjs-admin-template-free/issues"
>
Support
</Link>
</Box>
)}
</Box>
);
};
export default FooterContent;

View File

@@ -0,0 +1,45 @@
// ** MUI Imports
import Box from '@mui/material/Box';
import { useTheme } from '@mui/material/styles';
// ** Footer Content Component
import FooterContent from './FooterContent';
const Footer = props => {
// ** Props
const { settings, footerContent: userFooterContent } = props;
// ** Hook
const theme = useTheme();
// ** Vars
const { contentWidth } = settings;
return (
<Box
component="footer"
className="layout-footer"
sx={{
zIndex: 10,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Box
className="footer-content-container"
sx={{
width: '100%',
borderTopLeftRadius: 14,
borderTopRightRadius: 14,
padding: theme.spacing(4, 6),
...(contentWidth === 'boxed' && { '@media (min-width:1440px)': { maxWidth: 1440 } }),
}}
>
{userFooterContent ? userFooterContent(props) : <FooterContent />}
</Box>
</Box>
);
};
export default Footer;

View File

@@ -0,0 +1,56 @@
// ** MUI Imports
import { styled, useTheme } from '@mui/material/styles';
import MuiAppBar from '@mui/material/AppBar';
import MuiToolbar from '@mui/material/Toolbar';
const AppBar = styled(MuiAppBar)(({ theme }) => ({
transition: 'none',
alignItems: 'center',
justifyContent: 'center',
padding: theme.spacing(0, 6),
backgroundColor: 'transparent',
color: theme.palette.text.primary,
minHeight: theme.mixins.toolbar.minHeight,
[theme.breakpoints.down('sm')]: {
paddingLeft: theme.spacing(4),
paddingRight: theme.spacing(4),
},
}));
const Toolbar = styled(MuiToolbar)(({ theme }) => ({
width: '100%',
borderBottomLeftRadius: 10,
borderBottomRightRadius: 10,
padding: `${theme.spacing(0)} !important`,
minHeight: `${theme.mixins.toolbar.minHeight}px !important`,
transition:
'padding .25s ease-in-out, box-shadow .25s ease-in-out, backdrop-filter .25s ease-in-out, background-color .25s ease-in-out',
}));
const LayoutAppBar = props => {
// ** Props
const { settings, verticalAppBarContent: userVerticalAppBarContent } = props;
// ** Hooks
const theme = useTheme();
// ** Vars
const { contentWidth } = settings;
return (
<AppBar elevation={0} color="default" className="layout-navbar" position="static">
<Toolbar
className="navbar-content-container"
sx={{
...(contentWidth === 'boxed' && {
'@media (min-width:1440px)': { maxWidth: `calc(1440px - ${theme.spacing(6)} * 2)` },
}),
}}
>
{(userVerticalAppBarContent && userVerticalAppBarContent(props)) || null}
</Toolbar>
</AppBar>
);
};
export default LayoutAppBar;

View File

@@ -0,0 +1,66 @@
// ** MUI Imports
import { styled, useTheme } from '@mui/material/styles';
import MuiSwipeableDrawer from '@mui/material/SwipeableDrawer';
const SwipeableDrawer = styled(MuiSwipeableDrawer)({
overflowX: 'hidden',
transition: 'width .25s ease-in-out',
'& ul': {
listStyle: 'none',
},
'& .MuiListItem-gutters': {
paddingLeft: 4,
paddingRight: 4,
},
'& .MuiDrawer-paper': {
left: 'unset',
right: 'unset',
overflowX: 'hidden',
transition: 'width .25s ease-in-out, box-shadow .25s ease-in-out',
},
});
const Drawer = props => {
// ** Props
const { hidden, children, navWidth, navVisible, setNavVisible } = props;
// ** Hook
const theme = useTheme();
// Drawer Props for Mobile & Tablet screens
const MobileDrawerProps = {
open: navVisible,
onOpen: () => setNavVisible(true),
onClose: () => setNavVisible(false),
ModalProps: {
keepMounted: true, // Better open performance on mobile.
},
};
// Drawer Props for Desktop screens
const DesktopDrawerProps = {
open: true,
onOpen: () => null,
onClose: () => null,
};
return (
<SwipeableDrawer
className="layout-vertical-nav"
variant={hidden ? 'temporary' : 'permanent'}
{...(hidden ? { ...MobileDrawerProps } : { ...DesktopDrawerProps })}
PaperProps={{ sx: { width: navWidth } }}
sx={{
width: navWidth,
'& .MuiDrawer-paper': {
borderRight: 0,
backgroundColor: theme.palette.background.default,
},
}}
>
{children}
</SwipeableDrawer>
);
};
export default Drawer;

View File

@@ -0,0 +1,119 @@
// ** Next Import
import Link from 'next/link';
// ** MUI Imports
import Box from '@mui/material/Box';
import { styled, useTheme } from '@mui/material/styles';
import Typography from '@mui/material/Typography';
// ** Configs
import themeConfig from 'src/configs/themeConfig';
// ** Styled Components
const MenuHeaderWrapper = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
paddingRight: theme.spacing(4.5),
transition: 'padding .25s ease-in-out',
minHeight: theme.mixins.toolbar.minHeight,
}));
const HeaderTitle = styled(Typography)(({ theme }) => ({
fontWeight: 600,
lineHeight: 'normal',
textTransform: 'uppercase',
color: theme.palette.text.primary,
transition: 'opacity .25s ease-in-out, margin .25s ease-in-out',
}));
const StyledLink = styled('a')({
display: 'flex',
alignItems: 'center',
textDecoration: 'none',
});
const VerticalNavHeader = props => {
// ** Props
const { verticalNavMenuBranding: userVerticalNavMenuBranding } = props;
// ** Hooks
const theme = useTheme();
return (
<MenuHeaderWrapper className="nav-header" sx={{ pl: 6 }}>
{userVerticalNavMenuBranding ? (
userVerticalNavMenuBranding(props)
) : (
<Link href="/" passHref>
<StyledLink>
<svg
width={30}
height={25}
version="1.1"
viewBox="0 0 30 23"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<g stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
<g id="Artboard" transform="translate(-95.000000, -51.000000)">
<g id="logo" transform="translate(95.000000, 50.000000)">
<path
id="Combined-Shape"
fill={theme.palette.primary.main}
d="M30,21.3918362 C30,21.7535219 29.9019196,22.1084381 29.7162004,22.4188007 C29.1490236,23.366632 27.9208668,23.6752135 26.9730355,23.1080366 L26.9730355,23.1080366 L23.714971,21.1584295 C23.1114106,20.7972624 22.7419355,20.1455972 22.7419355,19.4422291 L22.7419355,19.4422291 L22.741,12.7425689 L15,17.1774194 L7.258,12.7425689 L7.25806452,19.4422291 C7.25806452,20.1455972 6.88858935,20.7972624 6.28502902,21.1584295 L3.0269645,23.1080366 C2.07913318,23.6752135 0.850976404,23.366632 0.283799571,22.4188007 C0.0980803893,22.1084381 2.0190442e-15,21.7535219 0,21.3918362 L0,3.58469444 L0.00548573643,3.43543209 L0.00548573643,3.43543209 L0,3.5715689 C3.0881846e-16,2.4669994 0.8954305,1.5715689 2,1.5715689 C2.36889529,1.5715689 2.73060353,1.67359571 3.04512412,1.86636639 L15,9.19354839 L26.9548759,1.86636639 C27.2693965,1.67359571 27.6311047,1.5715689 28,1.5715689 C29.1045695,1.5715689 30,2.4669994 30,3.5715689 L30,3.5715689 Z"
/>
<polygon
id="Rectangle"
opacity="0.077704"
fill={theme.palette.common.black}
points="0 8.58870968 7.25806452 12.7505183 7.25806452 16.8305646"
/>
<polygon
id="Rectangle"
opacity="0.077704"
fill={theme.palette.common.black}
points="0 8.58870968 7.25806452 12.6445567 7.25806452 15.1370162"
/>
<polygon
id="Rectangle"
opacity="0.077704"
fill={theme.palette.common.black}
points="22.7419355 8.58870968 30 12.7417372 30 16.9537453"
transform="translate(26.370968, 12.771227) scale(-1, 1) translate(-26.370968, -12.771227) "
/>
<polygon
id="Rectangle"
opacity="0.077704"
fill={theme.palette.common.black}
points="22.7419355 8.58870968 30 12.6409734 30 15.2601969"
transform="translate(26.370968, 11.924453) scale(-1, 1) translate(-26.370968, -11.924453) "
/>
<path
id="Rectangle"
fillOpacity="0.15"
fill={theme.palette.common.white}
d="M3.04512412,1.86636639 L15,9.19354839 L15,9.19354839 L15,17.1774194 L0,8.58649679 L0,3.5715689 C3.0881846e-16,2.4669994 0.8954305,1.5715689 2,1.5715689 C2.36889529,1.5715689 2.73060353,1.67359571 3.04512412,1.86636639 Z"
/>
<path
id="Rectangle"
fillOpacity="0.35"
fill={theme.palette.common.white}
transform="translate(22.500000, 8.588710) scale(-1, 1) translate(-22.500000, -8.588710) "
d="M18.0451241,1.86636639 L30,9.19354839 L30,9.19354839 L30,17.1774194 L15,8.58649679 L15,3.5715689 C15,2.4669994 15.8954305,1.5715689 17,1.5715689 C17.3688953,1.5715689 17.7306035,1.67359571 18.0451241,1.86636639 Z"
/>
</g>
</g>
</g>
</svg>
<HeaderTitle variant="h6" sx={{ ml: 3 }}>
{themeConfig.templateName}
</HeaderTitle>
</StyledLink>
</Link>
)}
</MenuHeaderWrapper>
);
};
export default VerticalNavHeader;

View File

@@ -0,0 +1,24 @@
// ** Custom Menu Components
import VerticalNavLink from './VerticalNavLink';
import VerticalNavSectionTitle from './VerticalNavSectionTitle';
const resolveNavItemComponent = item => {
if (item.sectionTitle) return VerticalNavSectionTitle;
return VerticalNavLink;
};
const VerticalNavItems = props => {
// ** Props
const { verticalNavItems } = props;
const RenderMenuItems = verticalNavItems?.map((item, index) => {
const TagName = resolveNavItemComponent(item);
return <TagName {...props} key={index} item={item} />;
});
return <>{RenderMenuItems}</>;
};
export default VerticalNavItems;

View File

@@ -0,0 +1,119 @@
// ** Next Imports
import Link from 'next/link';
import { useRouter } from 'next/router';
// ** MUI Imports
import Chip from '@mui/material/Chip';
import ListItem from '@mui/material/ListItem';
import { styled } from '@mui/material/styles';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemButton from '@mui/material/ListItemButton';
// ** Configs Import
import themeConfig from 'src/configs/themeConfig';
// ** Custom Components Imports
import UserIcon from 'src/layouts/components/UserIcon';
// ** Utils
import { handleURLQueries } from 'src/@core/layouts/utils';
// ** Styled Components
const MenuNavLink = styled(ListItemButton)(({ theme }) => ({
width: '100%',
borderTopRightRadius: 100,
borderBottomRightRadius: 100,
color: theme.palette.text.primary,
padding: theme.spacing(2.25, 3.5),
transition: 'opacity .25s ease-in-out',
'&.active, &.active:hover': {
boxShadow: theme.shadows[3],
backgroundImage: `linear-gradient(98deg, ${theme.palette.customColors.primaryGradient}, ${theme.palette.primary.main} 94%)`,
},
'&.active .MuiTypography-root, &.active .MuiSvgIcon-root': {
color: `${theme.palette.common.white} !important`,
},
}));
const MenuItemTextMetaWrapper = styled(Box)({
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
transition: 'opacity .25s ease-in-out',
...(themeConfig.menuTextTruncate && { overflow: 'hidden' }),
});
const VerticalNavLink = ({ item, navVisible, toggleNavVisibility }) => {
// ** Hooks
const router = useRouter();
const IconTag = item.icon;
const isNavLinkActive = () => {
if (router.pathname === item.path || handleURLQueries(router, item.path)) {
return true;
} else {
return false;
}
};
return (
<ListItem
disablePadding
className="nav-link"
disabled={item.disabled || false}
sx={{ mt: 1.5, px: '0 !important' }}
>
<Link passHref href={item.path === undefined ? '/' : `${item.path}`}>
<MenuNavLink
component={'a'}
className={isNavLinkActive() ? 'active' : ''}
{...(item.openInNewTab ? { target: '_blank' } : null)}
onClick={e => {
if (item.path === undefined) {
e.preventDefault();
e.stopPropagation();
}
if (navVisible) {
toggleNavVisibility();
}
}}
sx={{
pl: 5.5,
...(item.disabled ? { pointerEvents: 'none' } : { cursor: 'pointer' }),
}}
>
<ListItemIcon
sx={{
mr: 2.5,
color: 'text.primary',
transition: 'margin .25s ease-in-out',
}}
>
<UserIcon icon={IconTag} />
</ListItemIcon>
<MenuItemTextMetaWrapper>
<Typography {...(themeConfig.menuTextTruncate && { noWrap: true })}>{item.title}</Typography>
{item.badgeContent ? (
<Chip
label={item.badgeContent}
color={item.badgeColor || 'primary'}
sx={{
height: 20,
fontWeight: 500,
marginLeft: 1.25,
'& .MuiChip-label': { px: 1.5, textTransform: 'capitalize' },
}}
/>
) : null}
</MenuItemTextMetaWrapper>
</MenuNavLink>
</Link>
</ListItem>
);
};
export default VerticalNavLink;

View File

@@ -0,0 +1,63 @@
// ** MUI Imports
import Divider from '@mui/material/Divider';
import { styled, useTheme } from '@mui/material/styles';
import Typography from '@mui/material/Typography';
import MuiListSubheader from '@mui/material/ListSubheader';
// ** Styled Components
const ListSubheader = styled(props => <MuiListSubheader component="li" {...props} />)(({ theme }) => ({
lineHeight: 1,
display: 'flex',
position: 'relative',
marginTop: theme.spacing(7),
marginBottom: theme.spacing(2),
backgroundColor: 'transparent',
transition: 'padding-left .25s ease-in-out',
}));
const TypographyHeaderText = styled(Typography)(({ theme }) => ({
fontSize: '0.75rem',
lineHeight: 'normal',
letterSpacing: '0.21px',
textTransform: 'uppercase',
color: theme.palette.text.disabled,
fontWeight: theme.typography.fontWeightMedium,
}));
const VerticalNavSectionTitle = props => {
// ** Props
const { item } = props;
// ** Hook
const theme = useTheme();
return (
<ListSubheader
className="nav-section-title"
sx={{
px: 0,
py: 1.75,
color: theme.palette.text.disabled,
'& .MuiDivider-root:before, & .MuiDivider-root:after, & hr': {
borderColor: `rgba(${theme.palette.customColors.main}, 0.12)`,
},
}}
>
<Divider
textAlign="left"
sx={{
m: 0,
width: '100%',
lineHeight: 'normal',
textTransform: 'uppercase',
'&:before, &:after': { top: 7, transform: 'none' },
'& .MuiDivider-wrapper': { px: 2.5, fontSize: '0.75rem', letterSpacing: '0.21px' },
}}
>
<TypographyHeaderText noWrap>{item.sectionTitle}</TypographyHeaderText>
</Divider>
</ListSubheader>
);
};
export default VerticalNavSectionTitle;

View File

@@ -0,0 +1,131 @@
// ** React Import
import { useRef, useState } from 'react';
// ** MUI Import
import List from '@mui/material/List';
import Box from '@mui/material/Box';
import { styled, useTheme } from '@mui/material/styles';
// ** Third Party Components
import PerfectScrollbar from 'react-perfect-scrollbar';
// ** Component Imports
import Drawer from './Drawer';
import VerticalNavItems from './VerticalNavItems';
import VerticalNavHeader from './VerticalNavHeader';
// ** Util Import
import { hexToRGBA } from 'src/@core/utils/hex-to-rgba';
const StyledBoxForShadow = styled(Box)({
top: 50,
left: -8,
zIndex: 2,
height: 75,
display: 'none',
position: 'absolute',
pointerEvents: 'none',
width: 'calc(100% + 15px)',
'&.d-block': {
display: 'block',
},
});
const Navigation = props => {
// ** Props
const {
hidden,
afterVerticalNavMenuContent,
beforeVerticalNavMenuContent,
verticalNavMenuContent: userVerticalNavMenuContent,
} = props;
// ** States
const [groupActive, setGroupActive] = useState([]);
const [currentActiveGroup, setCurrentActiveGroup] = useState([]);
// ** Ref
const shadowRef = useRef(null);
// ** Hooks
const theme = useTheme();
// ** Fixes Navigation InfiniteScroll
const handleInfiniteScroll = ref => {
if (ref) {
// @ts-ignore
ref._getBoundingClientRect = ref.getBoundingClientRect;
ref.getBoundingClientRect = () => {
// @ts-ignore
const original = ref._getBoundingClientRect();
return { ...original, height: Math.floor(original.height) };
};
}
};
// ** Scroll Menu
const scrollMenu = container => {
container = hidden ? container.target : container;
if (shadowRef && container.scrollTop > 0) {
// @ts-ignore
if (!shadowRef.current.classList.contains('d-block')) {
// @ts-ignore
shadowRef.current.classList.add('d-block');
}
} else {
// @ts-ignore
shadowRef.current.classList.remove('d-block');
}
};
const ScrollWrapper = hidden ? Box : PerfectScrollbar;
return (
<Drawer {...props}>
<VerticalNavHeader {...props} />
<StyledBoxForShadow
ref={shadowRef}
sx={{
background: `linear-gradient(${theme.palette.background.default} 40%,${hexToRGBA(
theme.palette.background.default,
0.1,
)} 95%,${hexToRGBA(theme.palette.background.default, 0.05)})`,
}}
/>
<Box sx={{ height: '100%', position: 'relative', overflow: 'hidden' }}>
<ScrollWrapper
containerRef={ref => handleInfiniteScroll(ref)}
{...(hidden
? {
onScroll: container => scrollMenu(container),
sx: { height: '100%', overflowY: 'auto', overflowX: 'hidden' },
}
: {
options: { wheelPropagation: false },
onScrollY: container => scrollMenu(container),
})}
>
{beforeVerticalNavMenuContent ? beforeVerticalNavMenuContent(props) : null}
<Box sx={{ height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }}>
{userVerticalNavMenuContent ? (
userVerticalNavMenuContent(props)
) : (
<List className="nav-items" sx={{ transition: 'padding .25s ease', pr: 4.5 }}>
<VerticalNavItems
groupActive={groupActive}
setGroupActive={setGroupActive}
currentActiveGroup={currentActiveGroup}
setCurrentActiveGroup={setCurrentActiveGroup}
{...props}
/>
</List>
)}
</Box>
</ScrollWrapper>
</Box>
{afterVerticalNavMenuContent ? afterVerticalNavMenuContent(props) : null}
</Drawer>
);
};
export default Navigation;

View File

@@ -0,0 +1,16 @@
/**
* Check for URL queries as well for matching
* Current URL & Item Path
*
* @param item
* @param activeItem
*/
export const handleURLQueries = (router, path) => {
if (Object.keys(router.query).length && path) {
const arr = Object.keys(router.query);
return router.asPath.includes(path) && router.asPath.includes(router.query[arr[0]]) && path !== '/';
}
return false;
};

View File

@@ -0,0 +1,103 @@
// ** MUI imports
import { styled } from '@mui/material/styles';
const ApexChartWrapper = styled('div')(({ theme }) => ({
'& .apexcharts-canvas': {
"& line[stroke='transparent']": {
display: 'none',
},
'& .apexcharts-xaxis > line, & .apexcharts-yaxis > line': {
stroke: theme.palette.divider,
},
'& .apexcharts-xaxis-tick, & .apexcharts-yaxis-tick': {
stroke: theme.palette.divider,
},
'& .apexcharts-tooltip': {
boxShadow: theme.shadows[3],
borderColor: theme.palette.divider,
background: theme.palette.background.paper,
'& .apexcharts-tooltip-title': {
fontWeight: 600,
borderColor: theme.palette.divider,
background: theme.palette.background.paper,
},
'&.apexcharts-theme-dark': {
'& .apexcharts-tooltip-text-label, & .apexcharts-tooltip-text-value': {
color: theme.palette.common.white,
},
},
'& .bar-chart': {
padding: theme.spacing(2, 2.5),
},
},
'& .apexcharts-xaxistooltip': {
borderColor: theme.palette.divider,
background: theme.palette.mode === 'light' ? theme.palette.grey[50] : theme.palette.background.default,
'& .apexcharts-xaxistooltip-text': {
color: theme.palette.text.primary,
},
'&:after': {
borderBottomColor: theme.palette.mode === 'light' ? theme.palette.grey[50] : theme.palette.background.default,
},
'&:before': {
borderBottomColor: theme.palette.divider,
},
},
'& .apexcharts-yaxistooltip': {
borderColor: theme.palette.divider,
background: theme.palette.mode === 'light' ? theme.palette.grey[50] : theme.palette.background.default,
'& .apexcharts-yaxistooltip-text': {
color: theme.palette.text.primary,
},
'&:after': {
borderLeftColor: theme.palette.mode === 'light' ? theme.palette.grey[50] : theme.palette.background.default,
},
'&:before': {
borderLeftColor: theme.palette.divider,
},
},
'& .apexcharts-text, & .apexcharts-tooltip-text, & .apexcharts-datalabel-label, & .apexcharts-datalabel': {
filter: 'none',
fontWeight: 400,
fill: theme.palette.text.primary,
fontFamily: `${theme.typography.fontFamily} !important`,
},
'& .apexcharts-pie-label': {
filter: 'none',
fill: theme.palette.common.white,
},
'& .apexcharts-pie': {
'& .apexcharts-datalabel-label, .apexcharts-datalabel-value': {
fontSize: '1.5rem',
},
},
'& .apexcharts-marker': {
boxShadow: 'none',
},
'& .apexcharts-legend-series': {
margin: `${theme.spacing(0.75, 2)} !important`,
'& .apexcharts-legend-text': {
marginLeft: theme.spacing(0.75),
color: `${theme.palette.text.primary} !important`,
},
},
'& .apexcharts-xcrosshairs, & .apexcharts-ycrosshairs, & .apexcharts-gridline': {
stroke: theme.palette.divider,
},
'& .apexcharts-heatmap-rect': {
stroke: theme.palette.mode === 'light' ? theme.palette.background.paper : theme.palette.background.default,
},
'& .apexcharts-radialbar > g > g:first-of-type .apexcharts-radialbar-area': {
stroke: theme.palette.background.default,
},
'& .apexcharts-radar-series polygon': {
stroke: theme.palette.divider,
fill: theme.palette.background.paper,
},
'& .apexcharts-radar-series line': {
stroke: theme.palette.divider,
},
},
}));
export default ApexChartWrapper;

View File

@@ -0,0 +1,358 @@
// ** MUI imports
import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
// ** Util Import
import { hexToRGBA } from 'src/@core/utils/hex-to-rgba';
const DatePickerWrapper = styled(Box)(({ theme }) => {
return {
'& .react-datepicker-popper': {
zIndex: 5,
},
'& .react-datepicker-wrapper': {
width: '100%',
},
'& .react-datepicker': {
border: 'none',
boxShadow: theme.shadows[7],
padding: theme.spacing(2, 0),
color: theme.palette.text.primary,
borderRadius: theme.shape.borderRadius,
fontFamily: theme.typography.fontFamily,
backgroundColor: theme.palette.background.paper,
'& .react-datepicker__header': {
padding: 0,
border: 'none',
fontWeight: 'normal',
backgroundColor: theme.palette.background.paper,
'& .react-datepicker__day-name': {
margin: 0,
},
},
'& .react-datepicker-year-header': {
lineHeight: 2.1,
marginBottom: '0.5rem',
color: theme.palette.text.primary,
},
'& .react-datepicker__triangle': {
display: 'none',
},
'& > .react-datepicker__navigation': {
top: theme.spacing(3),
'&.react-datepicker__navigation--previous': {
border: 'none',
backgroundImage: `${"url('data:image/svg+xml,%3Csvg xmlns=\\'http://www.w3.org/2000/svg\\' style=\\'width:24px;height:24px\\' viewBox=\\'0 0 24 24\\'%3E%3Cpath fill=\\'currentColor\\' d=\\'M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z\\' /%3E%3C/svg%3E')"
.replace('currentColor', theme.palette.text.secondary)
.replace('#', '%23')}`,
height: '24px',
width: '24px',
'& .react-datepicker__navigation-icon': {
display: 'none',
},
},
'&.react-datepicker__navigation--next': {
border: 'none',
backgroundImage: `${"url('data:image/svg+xml,%3Csvg xmlns=\\'http://www.w3.org/2000/svg\\' style=\\'width:24px;height:24px\\' viewBox=\\'0 0 24 24\\'%3E%3Cpath fill=\\'currentColor\\' d=\\'M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z\\' /%3E%3C/svg%3E')"
.replace('currentColor', theme.palette.text.secondary)
.replace('#', '%23')}`,
height: '24px',
width: '24px',
'& .react-datepicker__navigation-icon': {
display: 'none',
},
},
'&.react-datepicker__navigation--next--with-time': {
right: '122px',
},
'&:focus, &:active': {
outline: 0,
},
},
'& .react-datepicker__current-month': {
lineHeight: 2.1,
fontSize: '1rem',
fontWeight: 'normal',
letterSpacing: '0.15px',
marginBottom: theme.spacing(2),
color: theme.palette.text.primary,
},
'& .react-datepicker__day-name': {
lineHeight: 1.5,
width: '2.25rem',
fontSize: '0.75rem',
letterSpacing: '0.4px',
color: theme.palette.text.secondary,
},
'& .react-datepicker__day': {
margin: 0,
width: '2.25rem',
lineHeight: 2.75,
height: '2.25rem',
borderRadius: '50%',
color: theme.palette.text.primary,
'&.react-datepicker__day--selected, &.react-datepicker__day--keyboard-selected': {
color: theme.palette.common.white,
backgroundColor: `${theme.palette.primary.main} !important`,
},
'&.react-datepicker__day--in-range, &.react-datepicker__day--in-selecting-range': {
borderRadius: 0,
color: theme.palette.primary.main,
backgroundColor: `${hexToRGBA(theme.palette.primary.main, 0.06)} !important`,
'&:empty': {
backgroundColor: 'transparent !important',
},
},
'&.react-datepicker__day--selected.react-datepicker__day--in-selecting-range.react-datepicker__day--selecting-range-start, &.react-datepicker__day--selected.react-datepicker__day--range-start.react-datepicker__day--in-range, &.react-datepicker__day--range-start':
{
borderTopLeftRadius: '50%',
borderBottomLeftRadius: '50%',
color: theme.palette.common.white,
backgroundColor: `${theme.palette.primary.main} !important`,
},
'&.react-datepicker__day--range-end': {
borderTopRightRadius: '50%',
borderBottomRightRadius: '50%',
color: theme.palette.common.white,
backgroundColor: `${theme.palette.primary.main} !important`,
},
'&:focus, &:active': {
outline: 0,
},
'&.react-datepicker__day--outside-month': {
height: 'auto',
},
'&.react-datepicker__day--outside-month, &.react-datepicker__day--disabled:not(.react-datepicker__day--selected)':
{
color: theme.palette.text.disabled,
'&:hover': {
backgroundColor: 'transparent',
},
},
'&.react-datepicker__day--highlighted, &.react-datepicker__day--highlighted:hover': {
color: theme.palette.success.main,
backgroundColor: hexToRGBA(theme.palette.success.main, 0.12),
},
'&.react-datepicker__day--today': {
fontWeight: 'normal',
},
},
'& .react-datepicker__header__dropdown': {
'& .react-datepicker__month-dropdown-container:not(:last-child)': {
marginRight: theme.spacing(8),
},
'& .react-datepicker__month-dropdown-container, & .react-datepicker__year-dropdown-container': {
marginBottom: theme.spacing(4),
},
'& .react-datepicker__month-read-view--selected-month, & .react-datepicker__year-read-view--selected-year': {
fontSize: '0.875rem',
marginRight: theme.spacing(1),
color: theme.palette.text.primary,
},
'& .react-datepicker__month-read-view:hover .react-datepicker__month-read-view--down-arrow, & .react-datepicker__year-read-view:hover .react-datepicker__year-read-view--down-arrow':
{
borderTopColor: theme.palette.text.secondary,
borderRightColor: theme.palette.text.secondary,
},
'& .react-datepicker__month-read-view--down-arrow, & .react-datepicker__year-read-view--down-arrow': {
top: 4,
borderTopColor: theme.palette.text.disabled,
borderRightColor: theme.palette.text.disabled,
},
'& .react-datepicker__month-dropdown, & .react-datepicker__year-dropdown': {
paddingTop: theme.spacing(1.5),
paddingBottom: theme.spacing(1.5),
borderColor: theme.palette.divider,
borderRadius: theme.shape.borderRadius,
backgroundColor: theme.palette.background.paper,
boxShadow: theme.palette.mode === 'light' ? theme.shadows[8] : theme.shadows[9],
},
'& .react-datepicker__month-option, & .react-datepicker__year-option': {
paddingTop: theme.spacing(0.5),
paddingBottom: theme.spacing(0.5),
'&:hover': {
backgroundColor: theme.palette.action.hover,
},
},
'& .react-datepicker__month-option.react-datepicker__month-option--selected_month': {
backgroundColor: hexToRGBA(theme.palette.primary.main, 0.08),
'&:hover': {
backgroundColor: hexToRGBA(theme.palette.primary.main, 0.12),
},
'& .react-datepicker__month-option--selected': {
display: 'none',
},
},
'& .react-datepicker__year-option.react-datepicker__year-option--selected_year': {
backgroundColor: hexToRGBA(theme.palette.primary.main, 0.08),
'&:hover': {
backgroundColor: hexToRGBA(theme.palette.primary.main, 0.12),
},
'& .react-datepicker__year-option--selected': {
display: 'none',
},
},
'& .react-datepicker__year-option': {
// TODO: Remove some of the following styles for arrow in Year dropdown when react-datepicker give arrows in Year dropdown
'& .react-datepicker__navigation--years-upcoming': {
width: 9,
height: 9,
borderStyle: 'solid',
borderWidth: '3px 3px 0 0',
transform: 'rotate(-45deg)',
borderTopColor: theme.palette.text.disabled,
borderRightColor: theme.palette.text.disabled,
margin: `${theme.spacing(2.75)} auto ${theme.spacing(0)}`,
},
'&:hover .react-datepicker__navigation--years-upcoming': {
borderTopColor: theme.palette.text.secondary,
borderRightColor: theme.palette.text.secondary,
},
'& .react-datepicker__navigation--years-previous': {
width: 9,
height: 9,
borderStyle: 'solid',
borderWidth: '0 0 3px 3px',
transform: 'rotate(-45deg)',
borderLeftColor: theme.palette.text.disabled,
borderBottomColor: theme.palette.text.disabled,
margin: `${theme.spacing(0)} auto ${theme.spacing(2.75)}`,
},
'&:hover .react-datepicker__navigation--years-previous': {
borderLeftColor: theme.palette.text.secondary,
borderBottomColor: theme.palette.text.secondary,
},
},
},
'& .react-datepicker__month': {
marginTop: theme.spacing(3),
},
[theme.breakpoints.down('sm')]: {
'& .react-datepicker__month': {
marginLeft: 0,
marginRight: 0,
marginBottom: 0,
},
},
'& .react-datepicker__month, & .react-datepicker__year': {
'& .react-datepicker__month-text, & .react-datepicker__year-text, & .react-datepicker__quarter-text': {
height: '2rem',
alignItems: 'center',
display: 'inline-flex',
justifyContent: 'center',
'&:hover': {
borderRadius: theme.shape.borderRadius,
},
'&:focus, &:active': {
outline: 0,
},
},
'& .react-datepicker__quarter--selected, & .react-datepicker__year-text--selected, & .react-datepicker__month--selected, & .react-datepicker__quarter-text--keyboard-selected, & .react-datepicker__month-text--keyboard-selected, & .react-datepicker__year-text--keyboard-selected':
{
color: theme.palette.common.white,
borderRadius: theme.shape.borderRadius,
backgroundColor: `${theme.palette.primary.main} !important`,
},
'& .react-datepicker__week-number': {
fontWeight: 600,
color: theme.palette.text.primary,
},
},
'& .react-datepicker__year-wrapper': {
maxWidth: 205,
justifyContent: 'center',
},
'& .react-datepicker__input-time-container': {
display: 'flex',
alignItems: 'center',
},
'& .react-datepicker__today-button': {
borderRadius: '1rem',
margin: '0 1rem 0.3rem',
color: theme.palette.common.white,
backgroundColor: theme.palette.primary.main,
},
// ** Time Picker
'& .react-datepicker__time-container': {
borderLeftColor: theme.palette.divider,
},
'&.react-datepicker--time-only, & .react-datepicker__time-container': {
width: '7rem',
padding: theme.spacing(1.2, 0),
'& .react-datepicker-time__header': {
marginBottom: theme.spacing(3),
color: theme.palette.text.primary,
fontSize: theme.typography.body2.fontSize,
},
'& .react-datepicker__time': {
background: theme.palette.background.paper,
'& .react-datepicker__time-box .react-datepicker__time-list-item--disabled': {
color: theme.palette.text.disabled,
},
},
'& .react-datepicker__time-list-item': {
lineHeight: 1.75,
height: 'auto !important',
marginLeft: theme.spacing(3.2),
marginRight: theme.spacing(1.2),
color: theme.palette.text.primary,
borderRadius: theme.shape.borderRadius,
'&:focus, &:active': {
outline: 0,
},
'&:hover': {
backgroundColor: `${theme.palette.action.hover} !important`,
},
'&.react-datepicker__time-list-item--selected': {
color: `${theme.palette.common.white} !important`,
backgroundColor: `${theme.palette.primary.main} !important`,
},
},
'& .react-datepicker__time-box': {
width: '100%',
},
'& .react-datepicker__time-list': {
'&::-webkit-scrollbar': {
width: 8,
},
/* Track */
'&::-webkit-scrollbar-track': {
background: theme.palette.background.paper,
},
/* Handle */
'&::-webkit-scrollbar-thumb': {
background: '#aaa',
borderRadius: '10px',
},
/* Handle on hover */
'&::-webkit-scrollbar-thumb:hover': {
background: '#999',
},
},
},
'&.react-datepicker--time-only .react-datepicker__time-container': {
width: 'calc(7rem - 2px)',
},
'& .react-datepicker__day:hover, & .react-datepicker__month-text:hover, & .react-datepicker__quarter-text:hover, & .react-datepicker__year-text:hover':
{
backgroundColor: theme.palette.action.hover,
},
},
'& .react-datepicker__close-icon': {
paddingRight: theme.spacing(4),
'&:after': {
width: 'unset',
height: 'unset',
fontSize: '1.5rem',
color: theme.palette.text.primary,
backgroundColor: 'transparent !important',
},
},
};
});
export default DatePickerWrapper;

View File

@@ -0,0 +1,49 @@
// ** MUI Imports
import CssBaseline from '@mui/material/CssBaseline';
import GlobalStyles from '@mui/material/GlobalStyles';
import { ThemeProvider, createTheme, responsiveFontSizes } from '@mui/material/styles';
// ** Theme Config
import themeConfig from 'src/configs/themeConfig';
// ** Theme Override Imports
import overrides from './overrides';
import typography from './typography';
// ** Theme
import themeOptions from './ThemeOptions';
// ** Global Styles
import GlobalStyling from './globalStyles';
const ThemeComponent = props => {
// ** Props
const { settings, children } = props;
// ** Merged ThemeOptions of Core and User
const coreThemeConfig = themeOptions(settings);
// ** Pass ThemeOptions to CreateTheme Function to create partial theme without component overrides
let theme = createTheme(coreThemeConfig);
// ** Continue theme creation and pass merged component overrides to CreateTheme function
theme = createTheme(theme, {
components: { ...overrides(theme) },
typography: { ...typography(theme) },
});
// ** Set responsive font sizes to true
if (themeConfig.responsiveFontSizes) {
theme = responsiveFontSizes(theme);
}
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<GlobalStyles styles={() => GlobalStyling(theme)} />
{children}
</ThemeProvider>
);
};
export default ThemeComponent;

View File

@@ -0,0 +1,54 @@
// ** MUI Theme Provider
import { deepmerge } from '@mui/utils';
// ** Theme Override Imports
import palette from './palette';
import spacing from './spacing';
import shadows from './shadows';
import breakpoints from './breakpoints';
const themeOptions = settings => {
// ** Vars
const { mode, themeColor } = settings;
const themeConfig = {
palette: palette(mode, themeColor),
typography: {
fontFamily: [
'Inter',
'sans-serif',
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Roboto',
'"Helvetica Neue"',
'Arial',
'sans-serif',
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
].join(','),
},
shadows: shadows(mode),
...spacing,
breakpoints: breakpoints(),
shape: {
borderRadius: 6,
},
mixins: {
toolbar: {
minHeight: 64,
},
},
};
return deepmerge(themeConfig, {
palette: {
primary: {
...themeConfig.palette[themeColor],
},
},
});
};
export default themeOptions;

View File

@@ -0,0 +1,11 @@
const breakpoints = () => ({
values: {
xs: 0,
sm: 600,
md: 900,
lg: 1200,
xl: 1536,
},
});
export default breakpoints;

View File

@@ -0,0 +1,43 @@
const GlobalStyles = theme => {
return {
'.ps__rail-y': {
zIndex: 1,
right: '0 !important',
left: 'auto !important',
'&:hover, &:focus, &.ps--clicking': {
backgroundColor: theme.palette.mode === 'light' ? '#E4E5EB !important' : '#423D5D !important',
},
'& .ps__thumb-y': {
right: '3px !important',
left: 'auto !important',
backgroundColor: theme.palette.mode === 'light' ? '#C2C4D1 !important' : '#504B6D !important',
},
'.layout-vertical-nav &': {
'& .ps__thumb-y': {
width: 4,
backgroundColor: theme.palette.mode === 'light' ? '#C2C4D1 !important' : '#504B6D !important',
},
'&:hover, &:focus, &.ps--clicking': {
backgroundColor: 'transparent !important',
'& .ps__thumb-y': {
width: 6,
},
},
},
},
'#nprogress': {
pointerEvents: 'none',
'& .bar': {
left: 0,
top: 0,
height: 3,
width: '100%',
zIndex: 2000,
position: 'fixed',
backgroundColor: theme.palette.primary.main,
},
},
};
};
export default GlobalStyles;

View File

@@ -0,0 +1,46 @@
const Accordion = theme => {
return {
MuiAccordion: {
styleOverrides: {
root: {
'&.Mui-disabled': {
backgroundColor: `rgba(${theme.palette.customColors.main}, 0.12)`,
},
'&.Mui-expanded': {
boxShadow: theme.shadows[3],
},
},
},
},
MuiAccordionSummary: {
styleOverrides: {
root: {
padding: `0 ${theme.spacing(5)}`,
'& + .MuiCollapse-root': {
'& .MuiAccordionDetails-root:first-child': {
paddingTop: 0,
},
},
},
content: {
margin: `${theme.spacing(2.5)} 0`,
},
expandIconWrapper: {
color: theme.palette.text.secondary,
},
},
},
MuiAccordionDetails: {
styleOverrides: {
root: {
padding: theme.spacing(5),
'& + .MuiAccordionDetails-root': {
paddingTop: 0,
},
},
},
},
};
};
export default Accordion;

View File

@@ -0,0 +1,110 @@
import { lighten, darken } from '@mui/material/styles';
// ** Util Import
import { hexToRGBA } from 'src/@core/utils/hex-to-rgba';
const Alert = theme => {
const getColor = theme.palette.mode === 'light' ? darken : lighten;
return {
MuiAlert: {
styleOverrides: {
root: {
borderRadius: 5,
'& .MuiAlertTitle-root': {
marginBottom: theme.spacing(1.6),
},
'& a': {
color: 'inherit',
fontWeight: 500,
},
},
standardSuccess: {
color: getColor(theme.palette.success.main, 0.12),
backgroundColor: hexToRGBA(theme.palette.success.main, 0.12),
'& .MuiAlertTitle-root': {
color: getColor(theme.palette.success.main, 0.12),
},
'& .MuiAlert-icon': {
color: getColor(theme.palette.success.main, 0.12),
},
},
standardInfo: {
color: getColor(theme.palette.info.main, 0.12),
backgroundColor: hexToRGBA(theme.palette.info.main, 0.12),
'& .MuiAlertTitle-root': {
color: getColor(theme.palette.info.main, 0.12),
},
'& .MuiAlert-icon': {
color: getColor(theme.palette.info.main, 0.12),
},
},
standardWarning: {
color: getColor(theme.palette.warning.main, 0.12),
backgroundColor: hexToRGBA(theme.palette.warning.main, 0.12),
'& .MuiAlertTitle-root': {
color: getColor(theme.palette.warning.main, 0.12),
},
'& .MuiAlert-icon': {
color: getColor(theme.palette.warning.main, 0.12),
},
},
standardError: {
color: getColor(theme.palette.error.main, 0.12),
backgroundColor: hexToRGBA(theme.palette.error.main, 0.12),
'& .MuiAlertTitle-root': {
color: getColor(theme.palette.error.main, 0.12),
},
'& .MuiAlert-icon': {
color: getColor(theme.palette.error.main, 0.12),
},
},
outlinedSuccess: {
borderColor: theme.palette.success.main,
color: getColor(theme.palette.success.main, 0.12),
'& .MuiAlertTitle-root': {
color: getColor(theme.palette.success.main, 0.12),
},
'& .MuiAlert-icon': {
color: getColor(theme.palette.success.main, 0.12),
},
},
outlinedInfo: {
borderColor: theme.palette.info.main,
color: getColor(theme.palette.info.main, 0.12),
'& .MuiAlertTitle-root': {
color: getColor(theme.palette.info.main, 0.12),
},
'& .MuiAlert-icon': {
color: getColor(theme.palette.info.main, 0.12),
},
},
outlinedWarning: {
borderColor: theme.palette.warning.main,
color: getColor(theme.palette.warning.main, 0.12),
'& .MuiAlertTitle-root': {
color: getColor(theme.palette.warning.main, 0.12),
},
'& .MuiAlert-icon': {
color: getColor(theme.palette.warning.main, 0.12),
},
},
outlinedError: {
borderColor: theme.palette.error.main,
color: getColor(theme.palette.error.main, 0.12),
'& .MuiAlertTitle-root': {
color: getColor(theme.palette.error.main, 0.12),
},
'& .MuiAlert-icon': {
color: getColor(theme.palette.error.main, 0.12),
},
},
filled: {
fontWeight: 400,
},
},
},
};
};
export default Alert;

View File

@@ -0,0 +1,27 @@
const Avatar = theme => {
return {
MuiAvatar: {
styleOverrides: {
colorDefault: {
color: theme.palette.text.secondary,
backgroundColor: theme.palette.mode === 'light' ? theme.palette.grey[200] : theme.palette.grey[700],
},
rounded: {
borderRadius: 5,
},
},
},
MuiAvatarGroup: {
styleOverrides: {
root: {
justifyContent: 'flex-end',
'.MuiCard-root & .MuiAvatar-root': {
borderColor: theme.palette.background.paper,
},
},
},
},
};
};
export default Avatar;

View File

@@ -0,0 +1,22 @@
// ** Util Import
import { hexToRGBA } from 'src/@core/utils/hex-to-rgba';
const Backdrop = theme => {
return {
MuiBackdrop: {
styleOverrides: {
root: {
backgroundColor:
theme.palette.mode === 'light'
? `rgba(${theme.palette.customColors.main}, 0.7)`
: hexToRGBA(theme.palette.background.default, 0.7),
},
invisible: {
backgroundColor: 'transparent',
},
},
},
};
};
export default Backdrop;

View File

@@ -0,0 +1,50 @@
// ** Theme Config Imports
import themeConfig from 'src/configs/themeConfig';
const Button = theme => {
return {
MuiButton: {
styleOverrides: {
root: {
fontWeight: 500,
borderRadius: 5,
lineHeight: 1.71,
letterSpacing: '0.3px',
padding: `${theme.spacing(1.875, 3)}`,
},
contained: {
boxShadow: theme.shadows[3],
padding: `${theme.spacing(1.875, 5.5)}`,
},
outlined: {
padding: `${theme.spacing(1.625, 5.25)}`,
},
sizeSmall: {
padding: `${theme.spacing(1, 2.25)}`,
'&.MuiButton-contained': {
padding: `${theme.spacing(1, 3.5)}`,
},
'&.MuiButton-outlined': {
padding: `${theme.spacing(0.75, 3.25)}`,
},
},
sizeLarge: {
padding: `${theme.spacing(2.125, 5.5)}`,
'&.MuiButton-contained': {
padding: `${theme.spacing(2.125, 6.5)}`,
},
'&.MuiButton-outlined': {
padding: `${theme.spacing(1.875, 6.25)}`,
},
},
},
},
MuiButtonBase: {
defaultProps: {
disableRipple: themeConfig.disableRipple,
},
},
};
};
export default Button;

View File

@@ -0,0 +1,83 @@
const Card = theme => {
return {
MuiCard: {
styleOverrides: {
root: {
boxShadow: theme.shadows[6],
'& .card-more-options': {
marginTop: theme.spacing(-1),
marginRight: theme.spacing(-3),
},
},
},
},
MuiCardHeader: {
styleOverrides: {
root: {
padding: theme.spacing(5),
'& + .MuiCardContent-root, & + .MuiCollapse-root .MuiCardContent-root': {
paddingTop: 0,
},
'& .MuiCardHeader-subheader': {
fontSize: '0.875rem',
},
},
title: {
lineHeight: 1,
fontWeight: 500,
fontSize: '1.25rem',
letterSpacing: '0.0125em',
},
action: {
marginTop: 0,
marginRight: 0,
},
},
},
MuiCardContent: {
styleOverrides: {
root: {
padding: theme.spacing(5),
'& + .MuiCardContent-root': {
paddingTop: 0,
},
'&:last-of-type': {
paddingBottom: theme.spacing(5),
},
'& + .MuiCardActions-root': {
paddingTop: 0,
},
},
},
},
MuiCardActions: {
styleOverrides: {
root: {
padding: theme.spacing(5),
'&.card-action-dense': {
padding: theme.spacing(0, 2.5, 2.5),
'.MuiCard-root .MuiCardMedia-root + &': {
paddingTop: theme.spacing(2.5),
},
'.MuiCard-root &:first-of-type': {
paddingTop: theme.spacing(5),
paddingBottom: theme.spacing(5),
'& + .MuiCardContent-root': {
paddingTop: 0,
},
'& + .MuiCardHeader-root': {
paddingTop: 0,
},
},
},
'& .MuiButton-text': {
paddingLeft: theme.spacing(2.5),
paddingRight: theme.spacing(2.5),
},
},
},
},
};
};
export default Card;

View File

@@ -0,0 +1,19 @@
const Chip = theme => {
return {
MuiChip: {
styleOverrides: {
outlined: {
'&.MuiChip-colorDefault': {
borderColor: `rgba(${theme.palette.customColors.main}, 0.22)`,
},
},
deleteIcon: {
width: 18,
height: 18,
},
},
},
};
};
export default Chip;

View File

@@ -0,0 +1,61 @@
const DateTimePicker = theme => {
return {
MuiCalendarPicker: {
styleOverrides: {
root: {
'& [role="presentation"]': {
fontWeight: 400,
'& .PrivatePickersFadeTransitionGroup-root + .PrivatePickersFadeTransitionGroup-root > div': {
marginRight: 0,
},
'& .MuiIconButton-sizeSmall': {
padding: theme.spacing(0.5),
},
'& + div .MuiIconButton-root:not(.Mui-disabled)': {
color: theme.palette.text.secondary,
},
},
'& .PrivatePickersSlideTransition-root': {
minHeight: 240,
},
},
},
},
MuiPickersDay: {
styleOverrides: {
root: {
fontSize: '0.875rem',
},
},
},
MuiClockPicker: {
styleOverrides: {
arrowSwitcher: {
'& .MuiIconButton-root:not(.Mui-disabled)': {
color: theme.palette.text.secondary,
},
'& + div': {
'& > div': {
backgroundColor:
theme.palette.mode === 'light' ? theme.palette.grey[50] : theme.palette.background.default,
'& ~ .MuiIconButton-root span.MuiTypography-caption': {
color: 'inherit',
},
},
},
},
},
},
MuiMonthPicker: {
styleOverrides: {
root: {
'& > .MuiTypography-root.Mui-selected': {
fontSize: '1rem',
},
},
},
},
};
};
export default DateTimePicker;

View File

@@ -0,0 +1,104 @@
// ** Util Import
import { hexToRGBA } from 'src/@core/utils/hex-to-rgba';
const Dialog = theme => {
return {
MuiDialog: {
styleOverrides: {
paper: {
boxShadow: theme.shadows[6],
'&:not(.MuiDialog-paperFullScreen)': {
'@media (max-width:599px)': {
margin: theme.spacing(4),
width: `calc(100% - ${theme.spacing(8)})`,
maxWidth: `calc(100% - ${theme.spacing(8)}) !important`,
},
},
'& > .MuiList-root': {
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(1),
},
},
},
},
MuiDialogTitle: {
styleOverrides: {
root: {
padding: theme.spacing(5),
},
},
},
MuiDialogContent: {
styleOverrides: {
root: {
padding: theme.spacing(5),
'& + .MuiDialogContent-root': {
paddingTop: 0,
},
'& + .MuiDialogActions-root': {
paddingTop: 0,
},
// Styling for Mobile Date Picker starts
'& .PrivatePickersToolbar-root': {
padding: theme.spacing(4, 5),
color: theme.palette.primary.contrastText,
backgroundColor: theme.palette.primary.main,
'& .MuiTypography-root': {
color: theme.palette.primary.contrastText,
},
'& span.MuiTypography-overline': {
fontSize: '1rem',
lineHeight: '24px',
letterSpacing: '0.15px',
},
'& ~ div[class^="css-"] > div[class^="css-"]': {
marginTop: theme.spacing(6),
marginBottom: theme.spacing(6),
'& > div[class^="css-"]': {
backgroundColor:
theme.palette.mode === 'light' ? theme.palette.grey[50] : theme.palette.background.default,
'& ~ .MuiIconButton-root span.MuiTypography-caption': {
color: 'inherit',
},
},
},
'& .PrivateTimePickerToolbar-hourMinuteLabel': {
alignItems: 'center',
'& > .MuiButton-root span.MuiTypography-root': {
fontWeight: 300,
lineHeight: '72px',
fontSize: '3.75rem',
letterSpacing: '-0.5px',
},
'& > .MuiTypography-root': {
color: hexToRGBA(theme.palette.primary.contrastText, 0.54),
'& + .MuiButton-root > span.MuiTypography-root': {
color: hexToRGBA(theme.palette.primary.contrastText, 0.54),
},
},
},
'& .PrivateTimePickerToolbar-ampmSelection span.MuiTypography-root:not(.Mui-selected)': {
color: hexToRGBA(theme.palette.primary.contrastText, 0.54),
},
},
// Styling for Mobile Date Picker ends
},
},
},
MuiDialogActions: {
styleOverrides: {
root: {
padding: theme.spacing(5),
'&.dialog-actions-dense': {
padding: theme.spacing(2.5),
paddingTop: 0,
},
},
},
},
};
};
export default Dialog;

View File

@@ -0,0 +1,13 @@
const Divider = theme => {
return {
MuiDivider: {
styleOverrides: {
root: {
margin: `${theme.spacing(2)} 0`,
},
},
},
};
};
export default Divider;

View File

@@ -0,0 +1,85 @@
// ** Overrides Imports
import MuiCard from './card';
import MuiChip from './chip';
import MuiLink from './link';
import MuiList from './list';
import MuiMenu from './menu';
import MuiTabs from './tabs';
import MuiInput from './input';
import MuiPaper from './paper';
import MuiTable from './table';
import MuiAlerts from './alerts';
import MuiButton from './button';
import MuiDialog from './dialog';
import MuiRating from './rating';
import MuiSelect from './select';
import MuiAvatar from './avatars';
import MuiDivider from './divider';
import MuiPopover from './popover';
import MuiTooltip from './tooltip';
import MuiBackdrop from './backdrop';
import MuiSnackbar from './snackbar';
import MuiSwitches from './switches';
import MuiTimeline from './timeline';
import MuiAccordion from './accordion';
import MuiPagination from './pagination';
import MuiTypography from './typography';
import MuiToggleButton from './toggleButton';
import MuiDateTimePicker from './dateTimePicker';
const Overrides = theme => {
const chip = MuiChip(theme);
const list = MuiList(theme);
const menu = MuiMenu(theme);
const tabs = MuiTabs(theme);
const cards = MuiCard(theme);
const input = MuiInput(theme);
const tables = MuiTable(theme);
const alerts = MuiAlerts(theme);
const button = MuiButton(theme);
const rating = MuiRating(theme);
const avatars = MuiAvatar(theme);
const divider = MuiDivider(theme);
const dialog = MuiDialog(theme);
const popover = MuiPopover(theme);
const tooltip = MuiTooltip(theme);
const backdrop = MuiBackdrop(theme);
const snackbar = MuiSnackbar(theme);
const switches = MuiSwitches(theme);
const timeline = MuiTimeline(theme);
const accordion = MuiAccordion(theme);
const pagination = MuiPagination(theme);
const dateTimePicker = MuiDateTimePicker(theme);
return Object.assign(
chip,
list,
menu,
tabs,
cards,
input,
alerts,
button,
dialog,
rating,
tables,
avatars,
divider,
MuiLink,
popover,
tooltip,
backdrop,
MuiPaper,
snackbar,
switches,
timeline,
accordion,
MuiSelect,
pagination,
MuiTypography,
dateTimePicker,
MuiToggleButton,
);
};
export default Overrides;

View File

@@ -0,0 +1,62 @@
const input = theme => {
return {
MuiInputLabel: {
styleOverrides: {
root: {
color: theme.palette.text.secondary,
},
},
},
MuiInput: {
styleOverrides: {
root: {
'&:before': {
borderBottom: `1px solid rgba(${theme.palette.customColors.main}, 0.22)`,
},
'&:hover:not(.Mui-disabled):before': {
borderBottom: `1px solid rgba(${theme.palette.customColors.main}, 0.32)`,
},
'&.Mui-disabled:before': {
borderBottom: `1px solid ${theme.palette.text.disabled}`,
},
},
},
},
MuiFilledInput: {
styleOverrides: {
root: {
backgroundColor: `rgba(${theme.palette.customColors.main}, 0.04)`,
'&:hover:not(.Mui-disabled)': {
backgroundColor: `rgba(${theme.palette.customColors.main}, 0.08)`,
},
'&:before': {
borderBottom: `1px solid rgba(${theme.palette.customColors.main}, 0.22)`,
},
'&:hover:not(.Mui-disabled):before': {
borderBottom: `1px solid rgba(${theme.palette.customColors.main}, 0.32)`,
},
},
},
},
MuiOutlinedInput: {
styleOverrides: {
root: {
'&:hover:not(.Mui-focused) .MuiOutlinedInput-notchedOutline': {
borderColor: `rgba(${theme.palette.customColors.main}, 0.32)`,
},
'&:hover.Mui-error .MuiOutlinedInput-notchedOutline': {
borderColor: theme.palette.error.main,
},
'& .MuiOutlinedInput-notchedOutline': {
borderColor: `rgba(${theme.palette.customColors.main}, 0.22)`,
},
'&.Mui-disabled .MuiOutlinedInput-notchedOutline': {
borderColor: theme.palette.text.disabled,
},
},
},
},
};
};
export default input;

View File

@@ -0,0 +1,9 @@
export default {
MuiLink: {
styleOverrides: {
root: {
textDecoration: 'none',
},
},
},
};

View File

@@ -0,0 +1,41 @@
const List = theme => {
return {
MuiListItemIcon: {
styleOverrides: {
root: {
minWidth: 0,
marginRight: theme.spacing(2.25),
color: theme.palette.text.secondary,
},
},
},
MuiListItemAvatar: {
styleOverrides: {
root: {
minWidth: 0,
marginRight: theme.spacing(4),
},
},
},
MuiListItemText: {
styleOverrides: {
dense: {
'& .MuiListItemText-primary': {
color: theme.palette.text.primary,
},
},
},
},
MuiListSubheader: {
styleOverrides: {
root: {
fontWeight: 600,
textTransform: 'uppercase',
color: theme.palette.text.primary,
},
},
},
};
};
export default List;

View File

@@ -0,0 +1,16 @@
const Menu = theme => {
return {
MuiMenu: {
styleOverrides: {
root: {
'& .MuiMenu-paper': {
borderRadius: 5,
boxShadow: theme.palette.mode === 'light' ? theme.shadows[8] : theme.shadows[9],
},
},
},
},
};
};
export default Menu;

View File

@@ -0,0 +1,38 @@
// ** Util Import
import { hexToRGBA } from 'src/@core/utils/hex-to-rgba';
const Pagination = theme => {
return {
MuiPaginationItem: {
styleOverrides: {
root: {
'&.Mui-selected:not(.Mui-disabled):not(.MuiPaginationItem-textPrimary):not(.MuiPaginationItem-textSecondary):hover':
{
backgroundColor: `rgba(${theme.palette.customColors.main}, 0.12)`,
},
},
outlined: {
borderColor: `rgba(${theme.palette.customColors.main}, 0.22)`,
},
outlinedPrimary: {
'&.Mui-selected': {
backgroundColor: hexToRGBA(theme.palette.primary.main, 0.12),
'&:hover': {
backgroundColor: `${hexToRGBA(theme.palette.primary.main, 0.2)} !important`,
},
},
},
outlinedSecondary: {
'&.Mui-selected': {
backgroundColor: hexToRGBA(theme.palette.secondary.main, 0.12),
'&:hover': {
backgroundColor: `${hexToRGBA(theme.palette.secondary.main, 0.2)} !important`,
},
},
},
},
},
};
};
export default Pagination;

View File

@@ -0,0 +1,9 @@
export default {
MuiPaper: {
styleOverrides: {
root: {
backgroundImage: 'none',
},
},
},
};

View File

@@ -0,0 +1,15 @@
const Popover = theme => {
return {
MuiPopover: {
styleOverrides: {
root: {
'& .MuiPopover-paper': {
boxShadow: theme.shadows[6],
},
},
},
},
};
};
export default Popover;

View File

@@ -0,0 +1,13 @@
const Rating = theme => {
return {
MuiRating: {
styleOverrides: {
root: {
color: theme.palette.warning.main,
},
},
},
};
};
export default Rating;

View File

@@ -0,0 +1,12 @@
export default {
MuiSelect: {
styleOverrides: {
select: {
minWidth: '6rem !important',
'&.MuiTablePagination-select': {
minWidth: '1rem !important',
},
},
},
},
};

View File

@@ -0,0 +1,13 @@
const Snackbar = theme => {
return {
MuiSnackbarContent: {
styleOverrides: {
root: {
backgroundColor: theme.palette.mode === 'light' ? theme.palette.grey[900] : theme.palette.grey[100],
},
},
},
};
};
export default Snackbar;

View File

@@ -0,0 +1,15 @@
const Switch = theme => {
return {
MuiSwitch: {
styleOverrides: {
root: {
'& .MuiSwitch-track': {
backgroundColor: `rgb(${theme.palette.customColors.main})`,
},
},
},
},
};
};
export default Switch;

View File

@@ -0,0 +1,66 @@
const Table = theme => {
return {
MuiTableContainer: {
styleOverrides: {
root: {
boxShadow: theme.shadows[0],
borderTopColor: theme.palette.divider,
},
},
},
MuiTableHead: {
styleOverrides: {
root: {
textTransform: 'uppercase',
'& .MuiTableCell-head': {
fontSize: '0.75rem',
fontWeight: 600,
letterSpacing: '0.13px',
},
},
},
},
MuiTableBody: {
styleOverrides: {
root: {
'& .MuiTableCell-body': {
letterSpacing: '0.25px',
color: theme.palette.text.secondary,
'&:not(.MuiTableCell-sizeSmall):not(.MuiTableCell-paddingCheckbox):not(.MuiTableCell-paddingNone)': {
paddingTop: theme.spacing(3.5),
paddingBottom: theme.spacing(3.5),
},
},
},
},
},
MuiTableRow: {
styleOverrides: {
root: {
'& .MuiTableCell-head:first-child, & .MuiTableCell-root:first-child ': {
paddingLeft: theme.spacing(5),
},
'& .MuiTableCell-head:last-child, & .MuiTableCell-root:last-child': {
paddingRight: theme.spacing(5),
},
},
},
},
MuiTableCell: {
styleOverrides: {
root: {
borderBottom: `1px solid ${theme.palette.divider}`,
'& .MuiButton-root': {
textTransform: 'uppercase',
color: theme.palette.text.secondary,
},
},
stickyHeader: {
backgroundColor: theme.palette.customColors.tableHeaderBg,
},
},
},
};
};
export default Table;

View File

@@ -0,0 +1,27 @@
const Tabs = theme => {
return {
MuiTabs: {
styleOverrides: {
vertical: {
minWidth: 130,
marginRight: theme.spacing(4),
borderRight: `1px solid ${theme.palette.divider}`,
'& .MuiTab-root': {
minWidth: 130,
},
},
},
},
MuiTab: {
styleOverrides: {
textColorSecondary: {
'&.Mui-selected': {
color: theme.palette.text.secondary,
},
},
},
},
};
};
export default Tabs;

View File

@@ -0,0 +1,80 @@
// ** Util Import
import { hexToRGBA } from 'src/@core/utils/hex-to-rgba';
const Timeline = theme => {
return {
MuiTimelineItem: {
styleOverrides: {
root: {
'&:not(:last-of-type)': {
'& .MuiTimelineContent-root': {
marginBottom: theme.spacing(4),
},
},
},
},
},
MuiTimelineConnector: {
styleOverrides: {
root: {
backgroundColor: theme.palette.divider,
},
},
},
MuiTimelineContent: {
styleOverrides: {
root: {
marginTop: theme.spacing(0.5),
},
},
},
MuiTimelineDot: {
styleOverrides: {
filledPrimary: {
boxShadow: `0 0 0 3px ${hexToRGBA(theme.palette.primary.main, 0.12)}`,
},
filledSecondary: {
boxShadow: `0 0 0 3px ${hexToRGBA(theme.palette.secondary.main, 0.12)}`,
},
filledSuccess: {
boxShadow: `0 0 0 3px ${hexToRGBA(theme.palette.success.main, 0.12)}`,
},
filledError: {
boxShadow: `0 0 0 3px ${hexToRGBA(theme.palette.error.main, 0.12)}`,
},
filledWarning: {
boxShadow: `0 0 0 3px ${hexToRGBA(theme.palette.warning.main, 0.12)}`,
},
filledInfo: {
boxShadow: `0 0 0 3px ${hexToRGBA(theme.palette.info.main, 0.12)}`,
},
filledGrey: {
boxShadow: `0 0 0 3px ${hexToRGBA(theme.palette.grey[400], 0.12)}`,
},
outlinedPrimary: {
'& svg': { color: theme.palette.primary.main },
},
outlinedSecondary: {
'& svg': { color: theme.palette.secondary.main },
},
outlinedSuccess: {
'& svg': { color: theme.palette.success.main },
},
outlinedError: {
'& svg': { color: theme.palette.error.main },
},
outlinedWarning: {
'& svg': { color: theme.palette.warning.main },
},
outlinedInfo: {
'& svg': { color: theme.palette.info.main },
},
outlinedGrey: {
'& svg': { color: theme.palette.grey[500] },
},
},
},
};
};
export default Timeline;

View File

@@ -0,0 +1,16 @@
export default {
MuiToggleButtonGroup: {
styleOverrides: {
root: {
borderRadius: 4,
},
},
},
MuiToggleButton: {
styleOverrides: {
root: {
borderRadius: 4,
},
},
},
};

View File

@@ -0,0 +1,25 @@
// ** Util Import
import { hexToRGBA } from 'src/@core/utils/hex-to-rgba';
const Tooltip = theme => {
return {
MuiTooltip: {
styleOverrides: {
tooltip: {
backgroundColor:
theme.palette.mode === 'light'
? hexToRGBA(theme.palette.grey[900], 0.9)
: hexToRGBA(theme.palette.grey[700], 0.9),
},
arrow: {
color:
theme.palette.mode === 'light'
? hexToRGBA(theme.palette.grey[900], 0.9)
: hexToRGBA(theme.palette.grey[700], 0.9),
},
},
},
};
};
export default Tooltip;

View File

@@ -0,0 +1,13 @@
const Typography = theme => {
return {
MuiTypography: {
styleOverrides: {
gutterBottom: {
marginBottom: theme.spacing(2),
},
},
},
};
};
export default Typography;

View File

@@ -0,0 +1,107 @@
const DefaultPalette = (mode, themeColor) => {
// ** Vars
const lightColor = '58, 53, 65';
const darkColor = '231, 227, 252';
const mainColor = mode === 'light' ? lightColor : darkColor;
const primaryGradient = () => {
if (themeColor === 'primary') {
return 'rgba(12, 45, 87,0.7)';
} else if (themeColor === 'secondary') {
return '#9C9FA4';
} else if (themeColor === 'success') {
return '#93DD5C';
} else if (themeColor === 'error') {
return '#FF8C90';
} else if (themeColor === 'warning') {
return '#FFCF5C';
} else {
return '#6ACDFF';
}
};
return {
customColors: {
main: mainColor,
primaryGradient: primaryGradient(),
tableHeaderBg: mode === 'light' ? '#F9FAFC' : '#3D3759',
},
common: {
black: '#000',
white: '#FFF',
},
mode: mode,
primary: {
light: 'rgba(12, 45, 87,0.2)',
main: 'rgba(12, 45, 87,0.7)',
dark: 'rgba(12, 45, 87,1)',
contrastText: '#FFF',
},
secondary: {
light: '#9C9FA4',
main: '#8A8D93',
dark: '#777B82',
contrastText: '#FFF',
},
success: {
light: '#6AD01F',
main: '#56CA00',
dark: '#4CB200',
contrastText: '#FFF',
},
error: {
light: '#FF6166',
main: '#FF4C51',
dark: '#E04347',
contrastText: '#FFF',
},
warning: {
light: '#FFCA64',
main: '#FFB400',
dark: '#E09E00',
contrastText: '#FFF',
},
info: {
light: '#32BAFF',
main: '#16B1FF',
dark: '#139CE0',
contrastText: '#FFF',
},
grey: {
50: '#FAFAFA',
100: '#F5F5F5',
200: '#EEEEEE',
300: '#E0E0E0',
400: '#BDBDBD',
500: '#9E9E9E',
600: '#757575',
700: '#616161',
800: '#424242',
900: '#212121',
A100: '#D5D5D5',
A200: '#AAAAAA',
A400: '#616161',
A700: '#303030',
},
text: {
primary: `rgba(${mainColor}, 0.87)`,
secondary: `rgba(${mainColor}, 0.68)`,
disabled: `rgba(${mainColor}, 0.38)`,
},
divider: `rgba(${mainColor}, 0.12)`,
background: {
paper: mode === 'light' ? '#FFF' : '#312D4B',
default: mode === 'light' ? '#F4F5FA' : '#28243D',
},
action: {
active: `rgba(${mainColor}, 0.54)`,
hover: `rgba(${mainColor}, 0.04)`,
selected: `rgba(${mainColor}, 0.08)`,
disabled: `rgba(${mainColor}, 0.3)`,
disabledBackground: `rgba(${mainColor}, 0.18)`,
focus: `rgba(${mainColor}, 0.12)`,
},
};
};
export default DefaultPalette;

View File

@@ -0,0 +1,61 @@
const Shadows = mode => {
if (mode === 'light') {
return [
'none',
'0px 2px 1px -1px rgba(58, 53, 65, 0.2), 0px 1px 1px 0px rgba(58, 53, 65, 0.14), 0px 1px 3px 0px rgba(58, 53, 65, 0.12)',
'0px 3px 1px -2px rgba(58, 53, 65, 0.2), 0px 2px 2px 0px rgba(58, 53, 65, 0.14), 0px 1px 5px 0px rgba(58, 53, 65, 0.12)',
'0px 4px 8px -4px rgba(58, 53, 65, 0.42)',
'0px 6px 18px -8px rgba(58, 53, 65, 0.56)',
'0px 3px 5px -1px rgba(58, 53, 65, 0.2), 0px 5px 8px 0px rgba(58, 53, 65, 0.14), 0px 1px 14px 0px rgba(58, 53, 65, 0.12)',
'0px 2px 10px 0px rgba(58, 53, 65, 0.1)',
'0px 4px 5px -2px rgba(58, 53, 65, 0.2), 0px 7px 10px 1px rgba(58, 53, 65, 0.14), 0px 2px 16px 1px rgba(58, 53, 65, 0.12)',
'0px 5px 5px -3px rgba(58, 53, 65, 0.2), 0px 8px 10px 1px rgba(58, 53, 65, 0.14), 0px 3px 14px 2px rgba(58, 53, 65, 0.12)',
'0px 5px 6px -3px rgba(58, 53, 65, 0.2), 0px 9px 12px 1px rgba(58, 53, 65, 0.14), 0px 3px 16px 2px rgba(58, 53, 65, 0.12)',
'0px 6px 6px -3px rgba(58, 53, 65, 0.2), 0px 10px 14px 1px rgba(58, 53, 65, 0.14), 0px 4px 18px 3px rgba(58, 53, 65, 0.12)',
'0px 6px 7px -4px rgba(58, 53, 65, 0.2), 0px 11px 15px 1px rgba(58, 53, 65, 0.14), 0px 4px 20px 3px rgba(58, 53, 65, 0.12)',
'0px 7px 8px -4px rgba(58, 53, 65, 0.2), 0px 12px 17px 2px rgba(58, 53, 65, 0.14), 0px 5px 22px 4px rgba(58, 53, 65, 0.12)',
'0px 7px 8px -4px rgba(58, 53, 65, 0.2), 0px 13px 19px 2px rgba(58, 53, 65, 0.14), 0px 5px 24px 4px rgba(58, 53, 65, 0.12)',
'0px 7px 9px -4px rgba(58, 53, 65, 0.2), 0px 14px 21px 2px rgba(58, 53, 65, 0.14), 0px 5px 26px 4px rgba(58, 53, 65, 0.12)',
'0px 8px 9px -5px rgba(58, 53, 65, 0.2), 0px 15px 22px 2px rgba(58, 53, 65, 0.14), 0px 6px 28px 5px rgba(58, 53, 65, 0.12)',
'0px 8px 10px -5px rgba(58, 53, 65, 0.2), 0px 16px 24px 2px rgba(58, 53, 65, 0.14), 0px 6px 30px 5px rgba(58, 53, 65, 0.12)',
'0px 8px 11px -5px rgba(58, 53, 65, 0.2), 0px 17px 26px 2px rgba(58, 53, 65, 0.14), 0px 6px 32px 5px rgba(58, 53, 65, 0.12)',
'0px 9px 11px -5px rgba(58, 53, 65, 0.2), 0px 18px 28px 2px rgba(58, 53, 65, 0.14), 0px 7px 34px 6px rgba(58, 53, 65, 0.12)',
'0px 9px 12px -6px rgba(58, 53, 65, 0.2), 0px 19px 29px 2px rgba(58, 53, 65, 0.14), 0px 7px 36px 6px rgba(58, 53, 65, 0.12)',
'0px 10px 13px -6px rgba(58, 53, 65, 0.2), 0px 20px 31px 3px rgba(58, 53, 65, 0.14), 0px 8px 38px 7px rgba(58, 53, 65, 0.12)',
'0px 10px 13px -6px rgba(58, 53, 65, 0.2), 0px 21px 33px 3px rgba(58, 53, 65, 0.14), 0px 8px 40px 7px rgba(58, 53, 65, 0.12)',
'0px 10px 14px -6px rgba(58, 53, 65, 0.2), 0px 22px 35px 3px rgba(58, 53, 65, 0.14), 0px 8px 42px 7px rgba(58, 53, 65, 0.12)',
'0px 11px 14px -7px rgba(58, 53, 65, 0.2), 0px 23px 36px 3px rgba(58, 53, 65, 0.14), 0px 9px 44px 8px rgba(58, 53, 65, 0.12)',
'0px 11px 15px -7px rgba(58, 53, 65, 0.2), 0px 24px 38px 3px rgba(58, 53, 65, 0.14), 0px 9px 46px 8px rgba(58, 53, 65, 0.12)',
];
} else {
return [
'none',
'0px 2px 1px -1px rgba(19, 17, 32, 0.2), 0px 1px 1px 0px rgba(19, 17, 32, 0.14), 0px 1px 3px 0px rgba(19, 17, 32, 0.12)',
'0px 3px 1px -2px rgba(19, 17, 32, 0.2), 0px 2px 2px 0px rgba(19, 17, 32, 0.14), 0px 1px 5px 0px rgba(19, 17, 32, 0.12)',
'0px 4px 8px -4px rgba(19, 17, 32, 0.42)',
'0px 6px 18px -8px rgba(19, 17, 32, 0.56)',
'0px 3px 5px -1px rgba(19, 17, 32, 0.2), 0px 5px 8px rgba(19, 17, 32, 0.14), 0px 1px 14px rgba(19, 17, 32, 0.12)',
'0px 2px 10px 0px rgba(19, 17, 32, 0.1)',
'0px 4px 5px -2px rgba(19, 17, 32, 0.2), 0px 7px 10px 1px rgba(19, 17, 32, 0.14), 0px 2px 16px 1px rgba(19, 17, 32, 0.12)',
'0px 5px 5px -3px rgba(19, 17, 32, 0.2), 0px 8px 10px 1px rgba(19, 17, 32, 0.14), 0px 3px 14px 2px rgba(19, 17, 32, 0.12)',
'0px 5px 6px -3px rgba(19, 17, 32, 0.2), 0px 9px 12px 1px rgba(19, 17, 32, 0.14), 0px 3px 16px 2px rgba(19, 17, 32, 0.12)',
'0px 6px 6px -3px rgba(19, 17, 32, 0.2), 0px 10px 14px 1px rgba(19, 17, 32, 0.14), 0px 4px 18px 3px rgba(19, 17, 32, 0.12)',
'0px 6px 7px -4px rgba(19, 17, 32, 0.2), 0px 11px 15px 1px rgba(19, 17, 32, 0.14), 0px 4px 20px 3px rgba(19, 17, 32, 0.12)',
'0px 7px 8px -4px rgba(19, 17, 32, 0.2), 0px 12px 17px 2px rgba(19, 17, 32, 0.14), 0px 5px 22px 4px rgba(19, 17, 32, 0.12)',
'0px 7px 8px -4px rgba(19, 17, 32, 0.2), 0px 13px 19px 2px rgba(19, 17, 32, 0.14), 0px 5px 24px 4px rgba(19, 17, 32, 0.12)',
'0px 7px 9px -4px rgba(19, 17, 32, 0.2), 0px 14px 21px 2px rgba(19, 17, 32, 0.14), 0px 5px 26px 4px rgba(19, 17, 32, 0.12)',
'0px 8px 9px -5px rgba(19, 17, 32, 0.2), 0px 15px 22px 2px rgba(19, 17, 32, 0.14), 0px 6px 28px 5px rgba(19, 17, 32, 0.12)',
'0px 8px 10px -5px rgba(19, 17, 32, 0.2), 0px 16px 24px 2px rgba(19, 17, 32, 0.14), 0px 6px 30px 5px rgba(19, 17, 32, 0.12)',
'0px 8px 11px -5px rgba(19, 17, 32, 0.2), 0px 17px 26px 2px rgba(19, 17, 32, 0.14), 0px 6px 32px 5px rgba(19, 17, 32, 0.12)',
'0px 9px 11px -5px rgba(19, 17, 32, 0.2), 0px 18px 28px 2px rgba(19, 17, 32, 0.14), 0px 7px 34px 6px rgba(19, 17, 32, 0.12)',
'0px 9px 12px -6px rgba(19, 17, 32, 0.2), 0px 19px 29px 2px rgba(19, 17, 32, 0.14), 0px 7px 36px 6px rgba(19, 17, 32, 0.12)',
'0px 10px 13px -6px rgba(19, 17, 32, 0.2), 0px 20px 31px 3px rgba(19, 17, 32, 0.14), 0px 8px 38px 7px rgba(19, 17, 32, 0.12)',
'0px 10px 13px -6px rgba(19, 17, 32, 0.2), 0px 21px 33px 3px rgba(19, 17, 32, 0.14), 0px 8px 40px 7px rgba(19, 17, 32, 0.12)',
'0px 10px 14px -6px rgba(19, 17, 32, 0.2), 0px 22px 35px 3px rgba(19, 17, 32, 0.14), 0px 8px 42px 7px rgba(19, 17, 32, 0.12)',
'0px 11px 14px -7px rgba(19, 17, 32, 0.2), 0px 23px 36px 3px rgba(19, 17, 32, 0.14), 0px 9px 44px 8px rgba(19, 17, 32, 0.12)',
'0px 11px 15px -7px rgba(19, 17, 32, 0.2), 0px 24px 38px 3px rgba(19, 17, 32, 0.14), 0px 9px 46px 8px rgba(19, 17, 32, 0.12)',
];
}
};
export default Shadows;

View File

@@ -0,0 +1,3 @@
export default {
spacing: factor => `${0.25 * factor}rem`,
};

View File

@@ -0,0 +1,3 @@
export default {
spacing: factor => `${0.25 * factor}rem`
}

Some files were not shown because too many files have changed in this diff Show More