import
This commit is contained in:
34
.env
Normal file
34
.env
Normal file
@@ -0,0 +1,34 @@
|
||||
# AccManager Backend Configuration
|
||||
|
||||
# Application
|
||||
NODE_ENV=production
|
||||
|
||||
# Host port exposed by docker compose
|
||||
APP_PORT=3000
|
||||
|
||||
# Image used for server pull deployment
|
||||
DOCKER_IMAGE=toiiiiday/accmanager:1.2.1
|
||||
|
||||
# Container app port
|
||||
PORT=3000
|
||||
|
||||
# SQL Server Configuration
|
||||
DB_SERVER=172.20.235.176
|
||||
DB_USER=sa
|
||||
DB_PASSWORD=robotics@2022
|
||||
DB_NAME=AccManager
|
||||
DB_ENCRYPT=false
|
||||
DB_TRUST_CERTIFICATE=true
|
||||
DB_CONNECT_TIMEOUT=30000
|
||||
|
||||
# Security
|
||||
BCRYPT_ROUNDS=12
|
||||
|
||||
# Email Verification (SMTP)
|
||||
APP_BASE_URL=https://accrobot.pnkr.asia/
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_SECURE=false
|
||||
SMTP_USER=robotics.gitlab.2fa@gmail.com
|
||||
SMTP_PASS=jaljalhreomqruqo
|
||||
SMTP_FROM=robotics.gitlab.2fa@gmail.com
|
||||
1283
backend/server.js
1283
backend/server.js
File diff suppressed because it is too large
Load Diff
@@ -81,7 +81,51 @@ BEGIN
|
||||
END
|
||||
|
||||
-- ===========================================
|
||||
-- 4. CREATE AUDIT LOG TABLE
|
||||
-- 4. CREATE ASSET INVENTORY TABLE
|
||||
-- ===========================================
|
||||
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'AssetInventory')
|
||||
BEGIN
|
||||
CREATE TABLE AssetInventory (
|
||||
AssetId INT PRIMARY KEY IDENTITY(1,1),
|
||||
AssetCode NVARCHAR(100) NOT NULL UNIQUE,
|
||||
AssetName NVARCHAR(255) NOT NULL,
|
||||
Model NVARCHAR(255),
|
||||
SerialNumber NVARCHAR(100),
|
||||
Quantity INT NOT NULL DEFAULT 0,
|
||||
ImportInPeriod INT NOT NULL DEFAULT 0,
|
||||
ExportInPeriod INT NOT NULL DEFAULT 0,
|
||||
EndingBalance INT NOT NULL DEFAULT 0,
|
||||
Unit NVARCHAR(50),
|
||||
Department NVARCHAR(100),
|
||||
Project NVARCHAR(150),
|
||||
Location NVARCHAR(150),
|
||||
Custodian NVARCHAR(100),
|
||||
Borrower NVARCHAR(255),
|
||||
ExportedBy NVARCHAR(100),
|
||||
PurchaseDate DATE NULL,
|
||||
PurchasePrice DECIMAL(18,2) NULL,
|
||||
Status NVARCHAR(30) NOT NULL DEFAULT 'in_use',
|
||||
Notes NVARCHAR(MAX),
|
||||
CreatedBy INT NULL,
|
||||
CreatedDate DATETIME DEFAULT GETDATE(),
|
||||
UpdatedDate DATETIME DEFAULT GETDATE(),
|
||||
FOREIGN KEY (CreatedBy) REFERENCES Users(UserId) ON DELETE SET NULL
|
||||
);
|
||||
PRINT 'Table AssetInventory created successfully.';
|
||||
END
|
||||
|
||||
IF COL_LENGTH('dbo.AssetInventory', 'Borrower') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE AssetInventory ADD Borrower NVARCHAR(255) NULL;
|
||||
END
|
||||
|
||||
IF COL_LENGTH('dbo.AssetInventory', 'ExportedBy') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE AssetInventory ADD ExportedBy NVARCHAR(100) NULL;
|
||||
END
|
||||
|
||||
-- ===========================================
|
||||
-- 5. CREATE AUDIT LOG TABLE
|
||||
-- ===========================================
|
||||
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'AuditLog')
|
||||
BEGIN
|
||||
@@ -100,7 +144,7 @@ BEGIN
|
||||
END
|
||||
|
||||
-- ===========================================
|
||||
-- 5. CREATE INDEXES
|
||||
-- 6. CREATE INDEXES
|
||||
-- ===========================================
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_Users_Username')
|
||||
BEGIN
|
||||
@@ -117,10 +161,20 @@ BEGIN
|
||||
CREATE INDEX IX_Accounts_AppId ON Accounts(AppId);
|
||||
END
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_AssetInventory_AssetCode')
|
||||
BEGIN
|
||||
CREATE INDEX IX_AssetInventory_AssetCode ON AssetInventory(AssetCode);
|
||||
END
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_AssetInventory_Status')
|
||||
BEGIN
|
||||
CREATE INDEX IX_AssetInventory_Status ON AssetInventory(Status);
|
||||
END
|
||||
|
||||
PRINT 'Indexes created successfully.';
|
||||
|
||||
-- ===========================================
|
||||
-- 6. INSERT INITIAL DATA
|
||||
-- 7. INSERT INITIAL DATA
|
||||
-- ===========================================
|
||||
|
||||
-- Check if admin user exists
|
||||
@@ -144,7 +198,7 @@ BEGIN
|
||||
END
|
||||
|
||||
-- ===========================================
|
||||
-- 7. DISPLAY DATABASE INFORMATION
|
||||
-- 8. DISPLAY DATABASE INFORMATION
|
||||
-- ===========================================
|
||||
PRINT '';
|
||||
PRINT '========================================';
|
||||
|
||||
178
package-lock.json
generated
178
package-lock.json
generated
@@ -14,7 +14,9 @@
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"mssql": "^9.1.1",
|
||||
"nodemailer": "^8.0.4"
|
||||
"multer": "^2.1.1",
|
||||
"nodemailer": "^8.0.4",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
@@ -569,6 +571,15 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/adler-32": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
|
||||
"integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/agent-base": {
|
||||
"version": "7.1.4",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
||||
@@ -599,6 +610,12 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/append-field": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/arg": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||
@@ -906,6 +923,23 @@
|
||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
||||
"dependencies": {
|
||||
"streamsearch": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
@@ -993,6 +1027,19 @@
|
||||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/cfb": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
|
||||
"integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"adler-32": "~1.3.0",
|
||||
"crc-32": "~1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
@@ -1018,6 +1065,15 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/codepage": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
|
||||
"integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
|
||||
@@ -1027,6 +1083,21 @@
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
|
||||
"engines": [
|
||||
"node >= 6.0"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.0.2",
|
||||
"typedarray": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
@@ -1080,6 +1151,18 @@
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/crc-32": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
|
||||
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"crc32": "bin/crc32.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
@@ -1600,6 +1683,15 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/frac": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
|
||||
"integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
|
||||
@@ -2743,6 +2835,25 @@
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/multer": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz",
|
||||
"integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"append-field": "^1.0.0",
|
||||
"busboy": "^1.6.0",
|
||||
"concat-stream": "^2.0.0",
|
||||
"type-is": "^1.6.18"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.16.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/mz": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
||||
@@ -3769,6 +3880,18 @@
|
||||
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/ssf": {
|
||||
"version": "0.11.2",
|
||||
"resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
|
||||
"integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"frac": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
@@ -3801,6 +3924,14 @@
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/streamsearch": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
@@ -4223,6 +4354,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/typedarray": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unbox-primitive": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
|
||||
@@ -4406,6 +4543,45 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/wmf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
|
||||
"integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/word": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
|
||||
"integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/xlsx": {
|
||||
"version": "0.18.5",
|
||||
"resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
|
||||
"integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"adler-32": "~1.3.0",
|
||||
"cfb": "~1.2.1",
|
||||
"codepage": "~1.15.0",
|
||||
"crc-32": "~1.2.1",
|
||||
"ssf": "~0.11.2",
|
||||
"wmf": "~1.0.1",
|
||||
"word": "~0.3.0"
|
||||
},
|
||||
"bin": {
|
||||
"xlsx": "bin/xlsx.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.18.2",
|
||||
"mssql": "^9.1.1",
|
||||
"nodemailer": "^8.0.4"
|
||||
"multer": "^2.1.1",
|
||||
"nodemailer": "^8.0.4",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
|
||||
2277
public/js/app.js
2277
public/js/app.js
File diff suppressed because it is too large
Load Diff
@@ -197,3 +197,183 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add/Edit Asset Modal -->
|
||||
<div class="modal-backdrop fixed inset-0 z-[100] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="assetModal">
|
||||
<div class="modal-content w-full max-w-2xl bg-white rounded-xl shadow-2xl border border-slate-200 overflow-hidden m-4 flex flex-col" style="max-height: calc(100vh - 2rem);">
|
||||
<div class="px-6 py-4 border-b border-slate-100 flex items-center justify-between bg-slate-50">
|
||||
<h3 class="text-base font-extrabold text-slate-900">Biểu mẫu tài sản</h3>
|
||||
<button class="p-1.5 rounded-lg hover:bg-slate-200 text-slate-400 transition-colors" onclick="closeAssetModal()">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<form id="assetForm" class="p-6 space-y-4 overflow-y-auto">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Mã tài sản</label>
|
||||
<input type="text" id="assetCodeInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" required placeholder="TS-001">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Tên tài sản</label>
|
||||
<input type="text" id="assetNameInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" required placeholder="Laptop Dell Latitude 5440">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Trạng thái</label>
|
||||
<select id="assetStatusInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3">
|
||||
<option value="in_use">Đang sử dụng</option>
|
||||
<option value="in_stock">Trong kho</option>
|
||||
<option value="maintenance">Bảo trì</option>
|
||||
<option value="disposed">Thanh lý</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Model</label>
|
||||
<input type="text" id="assetModelInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" placeholder="Latitude 5440">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Số serial</label>
|
||||
<input type="text" id="assetSerialInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" placeholder="SN123...">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Số lượng (Tồn đầu kỳ)</label>
|
||||
<input type="number" id="assetQuantityInput" min="0" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" value="0">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Nhập trong kỳ</label>
|
||||
<input type="number" id="assetImportInPeriodInput" min="0" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" value="0">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Xuất trong kỳ</label>
|
||||
<input type="number" id="assetExportInPeriodInput" min="0" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50" value="0" readonly>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Tồn cuối kỳ</label>
|
||||
<input type="number" id="assetEndingBalanceInput" min="0" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50" value="0" readonly>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Đơn vị</label>
|
||||
<input type="text" id="assetUnitInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" placeholder="cái">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Phòng ban</label>
|
||||
<input type="text" id="assetDepartmentInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" placeholder="Kỹ thuật">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Dự án</label>
|
||||
<input type="text" id="assetProjectInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" placeholder="AGV / SS demo">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Vị trí</label>
|
||||
<input type="text" id="assetLocationInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" placeholder="Kho A">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Người phụ trách</label>
|
||||
<select id="assetCustodianInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3">
|
||||
<option value="">-- Chon nguoi phu trach --</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Người mượn hiện tại</label>
|
||||
<textarea id="assetBorrowerSummaryInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 h-24 resize-none bg-slate-50" readonly></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Ngày mua</label>
|
||||
<input type="date" id="assetPurchaseDateInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Giá mua</label>
|
||||
<input type="number" id="assetPriceInput" min="0" step="0.01" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" placeholder="0">
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Ghi chú</label>
|
||||
<textarea id="assetNotesInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 h-20 resize-none" placeholder="Thông tin bổ sung"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3 pt-2 sticky bottom-0 bg-white pb-1">
|
||||
<button type="button" class="flex-1 px-4 py-2 text-xs font-bold text-slate-600 border border-slate-200 rounded-lg" onclick="closeAssetModal()">Hủy</button>
|
||||
<button type="submit" class="flex-1 px-4 py-2 bg-primary hover:bg-primary-dim text-on-primary rounded-lg text-xs font-bold">Lưu tài sản</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Borrow Asset Modal -->
|
||||
<div class="modal-backdrop fixed inset-0 z-[100] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="borrowAssetModal">
|
||||
<div class="modal-content w-full max-w-lg bg-white rounded-xl shadow-2xl border border-slate-200 overflow-hidden m-4">
|
||||
<div class="px-6 py-4 border-b border-slate-100 flex items-center justify-between bg-slate-50">
|
||||
<h3 class="text-base font-extrabold text-slate-900">Mượn tài sản</h3>
|
||||
<button class="p-1.5 rounded-lg hover:bg-slate-200 text-slate-400 transition-colors" onclick="closeBorrowAssetModal()">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<form id="borrowAssetForm" class="p-6 space-y-4">
|
||||
<input type="hidden" id="borrowAssetIdInput">
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Tài sản</label>
|
||||
<input type="text" id="borrowAssetNameInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50" readonly>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Tồn cuối kỳ hiện tại</label>
|
||||
<input type="number" id="borrowCurrentEndingInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50" readonly>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Số lượng mượn</label>
|
||||
<input type="number" id="borrowQuantityInput" min="1" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" value="1" required>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Người mượn</label>
|
||||
<select id="borrowAssetUserInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" required>
|
||||
<option value="">-- Chon nguoi muon --</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Người xuất</label>
|
||||
<input type="text" id="borrowByInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50" readonly>
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Role người thao tác</label>
|
||||
<input type="text" id="borrowRoleInput" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50" readonly>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3 pt-2">
|
||||
<button type="button" class="flex-1 px-4 py-2 text-xs font-bold text-slate-600 border border-slate-200 rounded-lg" onclick="closeBorrowAssetModal()">Hủy</button>
|
||||
<button type="submit" class="flex-1 px-4 py-2 bg-primary hover:bg-primary-dim text-on-primary rounded-lg text-xs font-bold">Xác nhận mượn</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- View Asset Modal -->
|
||||
<div class="modal-backdrop fixed inset-0 z-[100] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="viewAssetModal">
|
||||
<div class="modal-content w-full max-w-2xl bg-white rounded-xl shadow-2xl border border-slate-200 overflow-hidden m-4 flex flex-col" style="max-height: calc(100vh - 2rem);">
|
||||
<div class="px-6 py-4 border-b border-slate-100 flex items-center justify-between bg-slate-50">
|
||||
<h3 class="text-base font-extrabold text-slate-900">Chi tiết tài sản</h3>
|
||||
<button class="p-1.5 rounded-lg hover:bg-slate-200 text-slate-400 transition-colors" onclick="closeViewAssetModal()">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="assetDetailsContent" class="p-6 grid grid-cols-1 md:grid-cols-2 gap-3 text-sm text-slate-700 overflow-y-auto"></div>
|
||||
<div class="p-6 pt-3 flex gap-3 border-t border-slate-100 bg-white">
|
||||
<button type="button" class="flex-1 px-4 py-2 text-xs font-bold text-slate-600 border border-slate-200 rounded-lg" onclick="closeViewAssetModal()">Đóng</button>
|
||||
<button type="button" class="flex-1 px-4 py-2 bg-primary hover:bg-primary-dim text-on-primary rounded-lg text-xs font-bold edit-asset-from-view">Sửa</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete Asset Modal -->
|
||||
<div class="modal-backdrop fixed inset-0 z-[100] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="deleteAssetModal">
|
||||
<div class="modal-content w-full max-w-md bg-white rounded-xl shadow-2xl border border-slate-200 overflow-hidden m-4">
|
||||
<div class="px-6 py-4 border-b border-slate-100 bg-red-50 flex items-center gap-3">
|
||||
<span class="material-symbols-outlined text-red-600 text-2xl">warning</span>
|
||||
<h3 class="text-base font-extrabold text-red-700">Xóa tài sản</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<p class="text-sm text-slate-600 mb-6">Bạn có chắc muốn xóa tài sản <strong id="deleteAssetName">-</strong>? Hành động này không thể hoàn tác.</p>
|
||||
<div class="flex gap-3">
|
||||
<button type="button" class="flex-1 px-4 py-2 text-xs font-bold text-slate-600 border border-slate-200 rounded-lg" onclick="closeDeleteAssetModal()">Hủy</button>
|
||||
<button type="button" class="flex-1 px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg text-xs font-bold confirm-delete-asset">Xóa</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>Robot Manager Account - Account Management System</title>
|
||||
<title>Robot Manager Account - Hệ thống quản lý</title>
|
||||
<!-- Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet"/>
|
||||
<!-- Material Symbols -->
|
||||
@@ -12,6 +12,7 @@
|
||||
<!-- Notiflix Notify -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/notiflix@3.2.7/dist/notiflix-3.2.7.min.css" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/notiflix@3.2.7/dist/notiflix-aio-3.2.7.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
|
||||
<style>
|
||||
.material-symbols-outlined {
|
||||
font-family: 'Material Symbols Outlined';
|
||||
@@ -44,6 +45,25 @@
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.tree-label {
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
color: #64748b;
|
||||
padding: 0 0.75rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.tree-branch {
|
||||
margin-left: 0.75rem;
|
||||
border-left: 1px dashed rgba(100, 116, 139, 0.5);
|
||||
padding-left: 0.6rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.2rem;
|
||||
}
|
||||
|
||||
body.app-shell {
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -194,23 +214,41 @@
|
||||
<div class="text-[10px] uppercase tracking-widest text-on-surface-variant mt-1.5 font-bold">Admin Console</div>
|
||||
</div>
|
||||
<!-- Primary Nav -->
|
||||
<nav class="flex-1 px-3 space-y-1">
|
||||
<a href="#dashboard" data-nav="dashboard" class="flex items-center gap-3 px-3 py-2 border-l-4 border-blue-600 bg-slate-200/80 dark:bg-slate-800 text-slate-900 dark:text-slate-50 font-bold group transition-all cursor-pointer">
|
||||
<nav class="flex-1 px-3 space-y-4">
|
||||
<div>
|
||||
<div class="tree-label">Tài khoản</div>
|
||||
<div class="tree-branch">
|
||||
<a href="#dashboard" data-nav="dashboard" class="flex items-center gap-3 px-3 py-2 border-l-4 border-blue-600 bg-slate-200/80 dark:bg-slate-800 text-slate-900 dark:text-slate-50 font-bold group transition-all cursor-pointer rounded-r-lg">
|
||||
<span class="material-symbols-outlined">dashboard</span>
|
||||
<span>Dashboard</span>
|
||||
<span>Tổng quan</span>
|
||||
</a>
|
||||
<a href="#applications" data-nav="applications" class="flex items-center gap-3 px-3 py-2 text-slate-600 dark:text-slate-400 hover:text-slate-900 hover:bg-slate-200/50 transition-all group cursor-pointer">
|
||||
<a href="#applications" data-nav="applications" class="flex items-center gap-3 px-3 py-2 text-slate-600 dark:text-slate-400 hover:text-slate-900 hover:bg-slate-200/50 transition-all group cursor-pointer rounded-r-lg">
|
||||
<span class="material-symbols-outlined">apps</span>
|
||||
<span>Applications</span>
|
||||
<span>Ứng dụng</span>
|
||||
</a>
|
||||
<a href="#accounts" data-nav="accounts" class="flex items-center gap-3 px-3 py-2 text-slate-600 dark:text-slate-400 hover:text-slate-900 hover:bg-slate-200/50 transition-all group cursor-pointer">
|
||||
<a href="#accounts" data-nav="accounts" class="flex items-center gap-3 px-3 py-2 text-slate-600 dark:text-slate-400 hover:text-slate-900 hover:bg-slate-200/50 transition-all group cursor-pointer rounded-r-lg">
|
||||
<span class="material-symbols-outlined">manage_accounts</span>
|
||||
<span>Accounts</span>
|
||||
<span>Tài khoản</span>
|
||||
</a>
|
||||
<a id="usersNav" href="#users" data-nav="users" class="flex items-center gap-3 px-3 py-2 text-slate-600 dark:text-slate-400 hover:text-slate-900 hover:bg-slate-200/50 transition-all group cursor-pointer" style="display: none;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="tree-label">Quản lý tài sản</div>
|
||||
<div class="tree-branch">
|
||||
<a href="#assets" data-nav="assets" class="flex items-center gap-3 px-3 py-2 text-slate-600 dark:text-slate-400 hover:text-slate-900 hover:bg-slate-200/50 transition-all group cursor-pointer rounded-r-lg">
|
||||
<span class="material-symbols-outlined">inventory_2</span>
|
||||
<span>Tài sản</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="usersSection" class="pt-2 border-t border-outline-variant/10" style="display: none;">
|
||||
<a id="usersNav" href="#users" data-nav="users" class="flex items-center gap-3 px-3 py-2 text-slate-600 dark:text-slate-400 hover:text-slate-900 hover:bg-slate-200/50 transition-all group cursor-pointer rounded-lg" style="display: none;">
|
||||
<span class="material-symbols-outlined">people</span>
|
||||
<span>Users</span>
|
||||
<span>Người dùng (Quản trị)</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
<!-- Footer -->
|
||||
<div class="px-6 pt-4 border-t border-outline-variant/10">
|
||||
@@ -219,26 +257,26 @@
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<button id="sidebarBackdrop" type="button" aria-label="Close navigation menu"></button>
|
||||
<button id="sidebarBackdrop" type="button" aria-label="Đóng menu điều hướng"></button>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main id="appMain" class="flex-1 flex flex-col h-screen min-w-0">
|
||||
<!-- TopAppBar -->
|
||||
<header class="h-14 flex items-center justify-between px-6 bg-slate-50/80 dark:bg-slate-950/80 backdrop-blur-xl border-b border-outline-variant/10 shrink-0">
|
||||
<div class="flex items-center gap-4 flex-1">
|
||||
<button id="mobileMenuBtn" type="button" class="p-2 rounded-lg text-slate-600 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors" aria-label="Open navigation menu" aria-controls="appSidebar" aria-expanded="false">
|
||||
<button id="mobileMenuBtn" type="button" class="p-2 rounded-lg text-slate-600 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors" aria-label="Mở menu điều hướng" aria-controls="appSidebar" aria-expanded="false">
|
||||
<span class="material-symbols-outlined">menu</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="topbar-actions flex items-center gap-4">
|
||||
<button id="profileBtn" type="button" class="profile-btn flex items-center gap-2 px-4 py-2 rounded-lg bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 dark:hover:bg-slate-700 transition-colors" title="Edit profile">
|
||||
<button id="profileBtn" type="button" class="profile-btn flex items-center gap-2 px-4 py-2 rounded-lg bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 dark:hover:bg-slate-700 transition-colors" title="Sửa hồ sơ">
|
||||
<span class="material-symbols-outlined text-slate-600 dark:text-slate-400">account_circle</span>
|
||||
<div class="profile-meta flex flex-col">
|
||||
<span id="accountUsername" class="text-xs font-semibold text-slate-900 dark:text-slate-50">User Account</span>
|
||||
<span id="accountRole" class="text-[10px] text-slate-500 dark:text-slate-400">Administrator</span>
|
||||
<span id="accountUsername" class="text-xs font-semibold text-slate-900 dark:text-slate-50">Tài khoản người dùng</span>
|
||||
<span id="accountRole" class="text-[10px] text-slate-500 dark:text-slate-400">Quản trị viên</span>
|
||||
</div>
|
||||
</button>
|
||||
<button id="logoutBtn" class="p-2 rounded-lg text-slate-600 dark:text-slate-300 hover:bg-red-100 dark:hover:bg-red-950 hover:text-red-700 dark:hover:text-red-300 transition-colors" title="Logout">
|
||||
<button id="logoutBtn" class="p-2 rounded-lg text-slate-600 dark:text-slate-300 hover:bg-red-100 dark:hover:bg-red-950 hover:text-red-700 dark:hover:text-red-300 transition-colors" title="Đăng xuất">
|
||||
<span class="material-symbols-outlined">logout</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -250,6 +288,6 @@
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="../js/app.js"></script>
|
||||
<script src="../js/app.js?v=20260421-4"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user