From 209b68c3f51786f24f108bb3f9b0abd8097a3291 Mon Sep 17 00:00:00 2001 From: DungTT Date: Tue, 31 Mar 2026 10:20:27 +0700 Subject: [PATCH] save --- .env | 2 +- codedialog.html | 224 +++++++++++++ database/run-setup.ps1 | 4 +- database/setup.sql | 2 + database/setup_output.log | 1 + js/app.js | 641 +++++++++++++++++++++++++++----------- modals.html | 187 +++++++++++ pages/accounts.html | 98 +++++- pages/applications.html | 85 ++++- server.js | 136 +++++++- setup_full_log.txt | Bin 0 -> 288 bytes setup_output.log | 53 ++++ 12 files changed, 1237 insertions(+), 196 deletions(-) create mode 100644 codedialog.html create mode 100644 database/setup_output.log create mode 100644 modals.html create mode 100644 setup_full_log.txt create mode 100644 setup_output.log diff --git a/.env b/.env index 13901f3..b2add71 100644 --- a/.env +++ b/.env @@ -6,7 +6,7 @@ PORT=3000 # SQL Server Configuration DB_SERVER=172.20.235.176 DB_USER=sa -DB_PASSWORD=robotics@2020 +DB_PASSWORD=robotics@2022 DB_NAME=AccManager DB_ENCRYPT=true DB_TRUST_CERTIFICATE=true diff --git a/codedialog.html b/codedialog.html new file mode 100644 index 0000000..455510a --- /dev/null +++ b/codedialog.html @@ -0,0 +1,224 @@ + + + + + +Sentinel Accounts - Account Details + + + + + + + + + +
+ +
+
Sentinel Accounts
+
+notifications +help_outline +
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+ +
+
+Resource Details +

Account Details

+
+ +
+ +
+ +
+
+cloud +
+
+
Cloud Infrastructure
+
AWS Production
+
+
+Active +
+
+ +
+ +
+ +
+alternate_email +admin.aws_prod + +
+
+ +
+ +
+lock +••••••••••••••• + +
+
+
+ +
+ +
+Alex Rivera Profile +
+
Account Owner
+
Alex Rivera
+
+
+ +
+
Date Created
+
+calendar_today + Oct 24, 2023 +
+
+
+
+ +
+ + +
+
+
+ \ No newline at end of file diff --git a/database/run-setup.ps1 b/database/run-setup.ps1 index 3d8dcfa..868f8e5 100644 --- a/database/run-setup.ps1 +++ b/database/run-setup.ps1 @@ -5,8 +5,8 @@ # SQL Server Connection Info $ServerName = "172.20.235.176" $Username = "sa" -$Password = "robotics@2020" -$SqlScriptPath = ".\setup.sql" +$Password = "robotics@2022" +$SqlScriptPath = Join-Path $PSScriptRoot "setup.sql" Write-Host "========================================" -ForegroundColor Cyan Write-Host "AccManager Database Setup" -ForegroundColor Cyan diff --git a/database/setup.sql b/database/setup.sql index 6feaa60..7f834b2 100644 --- a/database/setup.sql +++ b/database/setup.sql @@ -14,8 +14,10 @@ ELSE BEGIN PRINT 'Database AccManager already exists.'; END +GO USE AccManager; +GO -- =========================================== -- 1. CREATE USERS TABLE diff --git a/database/setup_output.log b/database/setup_output.log new file mode 100644 index 0000000..e0be56a --- /dev/null +++ b/database/setup_output.log @@ -0,0 +1 @@ +Sqlcmd: Error: Microsoft ODBC Driver 17 for SQL Server : Login failed for user 'sa'.. diff --git a/js/app.js b/js/app.js index 9085f9d..82a913f 100644 --- a/js/app.js +++ b/js/app.js @@ -11,44 +11,85 @@ class AccountManager { } this.currentUser = currentUser; - this.accounts = this.loadFromStorage('accounts') || []; - this.applications = this.loadFromStorage('applications') || [ - { id: 1, name: 'AWS', type: 'Cloud', status: 'online', icon: 'cloud' }, - { id: 2, name: 'GitHub', type: 'VCS', status: 'online', icon: 'code' }, - { id: 3, name: 'Google Workspace', type: 'Collaboration', status: 'online', icon: 'mail' }, - { id: 4, name: 'Nginx Proxy', type: 'Infra', status: 'offline', icon: 'dns' }, - ]; + this.accounts = []; + this.applications = []; + this.apiBase = '/api'; this.currentPage = 'dashboard'; - this.init(); + this.initPromise = this.init(); } - init() { + getUserId() { + const u = this.currentUser; + const detected = u?.UserId ?? u?.userId ?? u?.id ?? u?.ID ?? u?.userid ?? u?.user_id ?? u?.user?.UserId ?? u?.user?.userId; + // Fallback: if only username/role exist (no id), use default admin id = 1 + return detected ?? 1; + } + + async init() { + await this.fetchApplications(); + await this.fetchAccounts(); this.setupEventListeners(); - // Render dashboard content + this.loadModals(); // Load modals từ file riêng + // Render dashboard content (only for index.html) const mainContent = document.getElementById('mainContent'); - if (mainContent) { + if (mainContent && window.location.pathname.endsWith('index.html')) { mainContent.innerHTML = this.renderDashboard(); } } + async fetchApplications() { + const res = await fetch(`${this.apiBase}/applications`); + const data = await res.json(); + if (data.success) { + this.applications = data.data; + } else { + console.error('Load applications failed:', data.message); + } + } + + async fetchAccounts() { + const userId = this.getUserId(); + if (!userId) return; + const res = await fetch(`${this.apiBase}/accounts/user/${userId}`); + const data = await res.json(); + if (data.success) { + this.accounts = data.data; + } else { + console.error('Load accounts failed:', data.message); + } + } + + async loadModals() { + try { + const response = await fetch('../modals.html'); + const modalsHTML = await response.text(); + const modalsContainer = document.getElementById('modalsContainer'); + if (modalsContainer) { + modalsContainer.innerHTML = modalsHTML; + // Re-attach event listeners after modals are loaded + setTimeout(() => { + this.setupAccountRowListeners(); + this.setupFormListeners(); + }, 50); + } + } catch (error) { + console.error('Lỗi load modals:', error); + } + } + setupEventListeners() { - // Account modal - const addAccountBtn = document.getElementById('addAccountBtn'); - if (addAccountBtn) { - addAccountBtn.addEventListener('click', () => this.openAccountModal()); - } - - // Application modal - const addAppBtn = document.getElementById('addAppBtn'); - if (addAppBtn) { - addAppBtn.addEventListener('click', () => this.openAppModal()); - } - // Modal close buttons document.querySelectorAll('[data-close-modal]').forEach(btn => { btn.addEventListener('click', () => this.closeModals()); }); + // Close with Escape key + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape') { + this.closeModals(); + } + }); + // Form submissions const accountForm = document.getElementById('accountForm'); if (accountForm) { @@ -73,13 +114,34 @@ class AccountManager { this.setupAccountRowListeners(); } + setupFormListeners() { + const accountForm = document.getElementById('accountForm'); + if (accountForm) { + accountForm.addEventListener('submit', (e) => this.handleAccountSubmit(e)); + } + + const appForm = document.getElementById('appForm'); + if (appForm) { + appForm.addEventListener('submit', (e) => this.handleAppSubmit(e)); + } + + // Close when clicking backdrop outside modal content + document.querySelectorAll('.modal-backdrop').forEach(backdrop => { + backdrop.addEventListener('click', (evt) => { + if (evt.target === backdrop) { + this.closeModals(); + } + }); + }); + } + updateAccountDisplay() { // Use the logged-in user from constructor const usernameEl = document.getElementById('accountUsername'); const roleEl = document.getElementById('accountRole'); - - if (usernameEl) usernameEl.textContent = this.currentUser?.username || 'User'; - if (roleEl) roleEl.textContent = this.currentUser?.role || 'Administrator'; + + if (usernameEl) usernameEl.textContent = this.currentUser?.username || this.currentUser?.Username || 'User'; + if (roleEl) roleEl.textContent = this.currentUser?.role || this.currentUser?.Role || 'Administrator'; } handleLogout() { @@ -113,7 +175,7 @@ class AccountManager { Applications
${this.applications.length} - ${this.applications.filter(a => a.status === 'online').length} Active + ${this.applications.filter(a => (a.Status || a.status) === 'online').length} Active
@@ -148,14 +210,18 @@ class AccountManager {
${this.accounts.length > 0 ? `
- ${this.accounts.slice(-5).reverse().map(acc => ` + ${this.accounts.slice(-5).reverse().map(acc => { + const username = acc.AccountUsername || acc.username || '-'; + const service = acc.AppName || acc.service || '-'; + const owner = acc.Email || acc.owner || this.currentUser?.Username || this.currentUser?.username || '-'; + return `
-

${acc.username}

-

${acc.service} • ${acc.owner}

+

${username}

+

${service} • ${owner}

- `).join('')} + `;}).join('')}
` : `
@@ -188,7 +254,7 @@ class AccountManager { Service
@@ -208,18 +274,21 @@ class AccountManager { - ${this.accounts.map((acc, idx) => ` - - ${acc.owner} - ${acc.username} + ${this.accounts.map(acc => ` + + ${acc.Email || '-'} + ${acc.AccountUsername || '-'} - ${acc.service} + ${acc.AppName || '-'} - + - @@ -242,42 +311,6 @@ class AccountManager { - - `; } @@ -335,36 +368,43 @@ class AccountManager { Name Type + Description + URL Status Actions - ${this.applications.map((app, idx) => ` + ${this.applications.map(app => `
- ${app.icon} + ${app.Icon || 'apps'}
- ${app.name} + ${app.Name}
- ${app.type} + ${app.Type} + ${app.Description || '-'} + ${(app.Url || app.url) ? `${app.Url || app.url}` : '-'}
-
- ${app.status === 'online' ? 'Online' : 'Offline'} +
+ ${(app.Status || app.status) === 'online' ? 'Online' : 'Offline'}
- + -
@@ -377,86 +417,228 @@ class AccountManager { - - `; } setupAccountRowListeners() { + // View Account listeners + document.querySelectorAll('.view-account').forEach(btn => { + btn.addEventListener('click', (e) => { + const accountId = Number(btn.dataset.accountId); + const account = this.accounts.find(a => a.AccountId === accountId); + this.currentViewAccountId = accountId; + this.currentViewAccount = account; + document.getElementById('viewAccountService').textContent = account?.AppName || '-'; + document.getElementById('viewAccountOwner').textContent = account?.Email || '-'; + document.getElementById('viewAccountUsername').textContent = account?.AccountUsername || '-'; + document.getElementById('viewAccountPassword').textContent = '••••••••'; + document.getElementById('viewAccountPassword').dataset.visible = 'false'; + document.getElementById('toggleIcon').textContent = 'visibility'; + document.getElementById('viewAccountModal').classList.add('open'); + }); + }); + + // Delete Account listeners - show confirmation modal document.querySelectorAll('.delete-account').forEach(btn => { btn.addEventListener('click', (e) => { - const accountId = btn.dataset.accountId; - if (confirm('Delete this account?')) { - this.accounts.splice(accountId, 1); - this.saveToStorage('accounts', this.accounts); - location.href = './accounts.html'; + const accountId = Number(btn.dataset.accountId); + const account = this.accounts.find(a => a.AccountId === accountId); + this.pendingDeleteAccountId = accountId; + document.getElementById('deleteAccountUsername').textContent = account?.AccountUsername || ''; + document.getElementById('deleteAccountModal').classList.add('open'); + }); + }); + + // Confirm Delete Account + document.querySelectorAll('.confirm-delete-account').forEach(btn => { + btn.addEventListener('click', () => { + if (this.pendingDeleteAccountId !== undefined) { + fetch(`${this.apiBase}/accounts/${this.pendingDeleteAccountId}`, { method: 'DELETE' }) + .then(res => res.json()) + .then(data => { + if (data.success) { + alert('Account deleted successfully'); + this.closeModals(); + location.href = './accounts.html'; + } else { + alert(data.message || 'Delete account failed'); + } + }) + .catch(err => { + console.error(err); + alert('Delete account failed'); + }); } }); }); + // Edit Account listeners document.querySelectorAll('.edit-account').forEach(btn => { btn.addEventListener('click', (e) => { - const accountId = btn.dataset.accountId; - const account = this.accounts[accountId]; + const accountId = Number(btn.dataset.accountId); + const account = this.accounts.find(a => a.AccountId === accountId); // Populate form with existing data - document.getElementById('accountUsername').value = account.username; - document.getElementById('accountPassword').value = account.password; - document.getElementById('accountOwner').value = account.owner; - document.getElementById('accountService').value = account.service; - this.editingAccountId = accountId; + const form = document.getElementById('accountForm'); + if (form) { + const userInput = form.querySelector('#accountUsername'); + const passInput = form.querySelector('#accountPassword'); + const ownerInput = form.querySelector('#accountOwner'); + const serviceSelect = form.querySelector('#accountService'); + if (userInput) userInput.value = account?.AccountUsername || ''; + if (passInput) passInput.value = account?.AccountPassword || ''; + if (ownerInput) ownerInput.value = this.currentUser?.Username || this.currentUser?.username || ''; + if (serviceSelect) serviceSelect.value = account?.AppId || ''; + } + this.editingAccountId = account?.AccountId; + this.closeModals(); this.openAccountModal(); }); }); - document.querySelectorAll('.delete-app').forEach(btn => { - btn.addEventListener('click', (e) => { - const appId = btn.dataset.appId; - if (confirm('Delete this application?')) { - this.applications.splice(appId, 1); - this.saveToStorage('applications', this.applications); - location.href = './applications.html'; + // Edit from View modal + document.querySelectorAll('.edit-account-from-view').forEach(btn => { + btn.addEventListener('click', () => { + const account = this.currentViewAccount; + const form = document.getElementById('accountForm'); + if (form) { + const userInput = form.querySelector('#accountUsername'); + const passInput = form.querySelector('#accountPassword'); + const ownerInput = form.querySelector('#accountOwner'); + const serviceSelect = form.querySelector('#accountService'); + if (userInput) userInput.value = account?.AccountUsername || ''; + if (passInput) passInput.value = account?.AccountPassword || ''; + if (ownerInput) ownerInput.value = this.currentUser?.Username || this.currentUser?.username || ''; + if (serviceSelect) serviceSelect.value = account?.AppId || ''; + } + this.editingAccountId = account?.AccountId; + this.closeModals(); + this.openAccountModal(); + }); + }); + + // Toggle Password Visibility + document.querySelectorAll('.toggle-password').forEach(btn => { + btn.addEventListener('click', () => { + const passwordEl = document.getElementById('viewAccountPassword'); + const toggleIcon = document.getElementById('toggleIcon'); + const isVisible = passwordEl.dataset.visible === 'true'; + + if (isVisible) { + passwordEl.textContent = '••••••••'; + passwordEl.dataset.visible = 'false'; + toggleIcon.textContent = 'visibility'; + } else { + passwordEl.textContent = this.currentViewAccount?.AccountPassword || ''; + passwordEl.dataset.visible = 'true'; + toggleIcon.textContent = 'visibility_off'; } }); }); + // View App listeners + document.querySelectorAll('.view-app').forEach(btn => { + btn.addEventListener('click', (e) => { + const appId = Number(btn.dataset.appId); + const app = this.applications.find(a => a.AppId === appId); + this.currentViewAppId = appId; + document.getElementById('viewAppName').textContent = app?.Name || '-'; + document.getElementById('viewAppType').textContent = app?.Type || '-'; + document.getElementById('viewAppDescription').textContent = app?.Description || '-'; + const urlEl = document.getElementById('viewAppUrl'); + const urlVal = app?.Url || app?.url; + if (urlEl) { + if (urlVal) { + urlEl.innerHTML = `${urlVal}`; + } else { + urlEl.textContent = '-'; + } + } + const statusValue = app?.Status || app?.status; + document.getElementById('viewAppStatus').textContent = statusValue === 'online' ? 'Online' : 'Offline'; + document.getElementById('viewAppModal').classList.add('open'); + }); + }); + + // Delete App listeners - show confirmation modal + document.querySelectorAll('.delete-app').forEach(btn => { + btn.addEventListener('click', (e) => { + const appId = Number(btn.dataset.appId); + const app = this.applications.find(a => a.AppId === appId); + this.pendingDeleteAppId = appId; + document.getElementById('deleteAppName').textContent = app?.Name || ''; + document.getElementById('deleteAppModal').classList.add('open'); + }); + }); + + // Confirm Delete App + document.querySelectorAll('.confirm-delete-app').forEach(btn => { + btn.addEventListener('click', () => { + if (this.pendingDeleteAppId !== undefined) { + fetch(`${this.apiBase}/applications/${this.pendingDeleteAppId}`, { method: 'DELETE' }) + .then(res => res.json()) + .then(data => { + if (data.success) { + alert('Application deleted successfully'); + this.closeModals(); + location.href = './applications.html'; + } else { + alert(data.message || 'Delete application failed'); + } + }) + .catch(err => { + console.error(err); + alert('Delete application failed'); + }); + } + }); + }); + + // Edit App listeners document.querySelectorAll('.edit-app').forEach(btn => { btn.addEventListener('click', (e) => { - const appId = btn.dataset.appId; - const app = this.applications[appId]; - document.getElementById('appName').value = app.name; - document.getElementById('appType').value = app.type; - document.getElementById('appStatus').value = app.status; - this.editingAppId = appId; + const appId = Number(btn.dataset.appId); + const app = this.applications.find(a => a.AppId === appId); + document.getElementById('appName').value = app?.Name || ''; + document.getElementById('appType').value = app?.Type || ''; + document.getElementById('appStatus').value = app?.Status || 'online'; + document.getElementById('appDescription').value = app?.Description || ''; + document.getElementById('appUrl').value = app?.Url || app?.url || ''; + this.editingAppId = app?.AppId; + this.closeModals(); + this.openAppModal(); + }); + }); + + // Edit App from View modal + document.querySelectorAll('.edit-app-from-view').forEach(btn => { + btn.addEventListener('click', () => { + const appId = this.currentViewAppId; + const app = this.applications.find(a => a.AppId === appId); + document.getElementById('appName').value = app?.Name || ''; + document.getElementById('appType').value = app?.Type || ''; + document.getElementById('appStatus').value = app?.Status || 'online'; + document.getElementById('appDescription').value = app?.Description || ''; + document.getElementById('appUrl').value = app?.Url || ''; + this.editingAppId = app?.AppId; + this.closeModals(); + this.openAppModal(); + }); + }); + } + + setupAddButtonListeners() { + // Add Account button + document.querySelectorAll('#addAccountBtn').forEach(btn => { + btn.addEventListener('click', () => { + this.editingAccountId = undefined; + this.openAccountModal(); + }); + }); + + // Add Application button + document.querySelectorAll('#addAppBtn').forEach(btn => { + btn.addEventListener('click', () => { + this.editingAppId = undefined; this.openAppModal(); }); }); @@ -464,67 +646,139 @@ class AccountManager { handleAccountSubmit(e) { e.preventDefault(); - const newAccount = { - service: document.getElementById('accountService').value, - owner: document.getElementById('accountOwner').value, - username: document.getElementById('accountUsername').value, - password: document.getElementById('accountPassword').value, - dateCreated: new Date().toLocaleDateString() + const accountForm = document.getElementById('accountForm'); + const userId = this.getUserId(); + const appId = Number(accountForm?.querySelector('#accountService')?.value || 0); + const accountUsername = (accountForm?.querySelector('#accountUsername')?.value || '').trim(); + const accountPassword = (accountForm?.querySelector('#accountPassword')?.value || '').trim(); + const accountEmail = ((accountForm?.querySelector('#accountOwner')?.value || '').trim()) || this.currentUser?.Username || this.currentUser?.username || ''; + if (!accountForm) { + alert('Account form not found.'); + return; + } + if (!userId) { + alert('User is not authenticated. Please login again.'); + return; + } + if (!appId) { + alert('Please select a service.'); + return; + } + if (!accountUsername) { + alert('Please enter a username.'); + return; + } + if (!accountPassword) { + alert('Please enter a password.'); + return; + } + const payload = { + userId, + appId, + accountUsername, + accountPassword, + email: accountEmail, + accessLevel: 'user', + notes: '' }; - if (this.editingAccountId !== undefined) { - this.accounts[this.editingAccountId] = newAccount; - this.editingAccountId = undefined; - } else { - this.accounts.push(newAccount); - } + const isEdit = this.editingAccountId !== undefined; + const url = isEdit ? `${this.apiBase}/accounts/${this.editingAccountId}` : `${this.apiBase}/accounts`; + const method = isEdit ? 'PUT' : 'POST'; - this.saveToStorage('accounts', this.accounts); - this.closeModals(); - location.href = './accounts.html'; + fetch(url, { + method, + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }).then(res => res.json()).then(data => { + if (data.success) { + this.editingAccountId = undefined; + alert(isEdit ? 'Account updated successfully' : 'Account created successfully'); + this.closeModals(); + location.href = './accounts.html'; + } else { + alert(data.message || 'Save account failed'); + } + }).catch(err => { + console.error(err); + alert('Save account failed'); + }); } handleAppSubmit(e) { e.preventDefault(); - const newApp = { + const payload = { name: document.getElementById('appName').value, type: document.getElementById('appType').value, status: document.getElementById('appStatus').value, - icon: 'cloud' + icon: 'cloud', + description: document.getElementById('appDescription')?.value || '', + url: (document.getElementById('appUrl')?.value || '').trim() }; - if (this.editingAppId !== undefined) { - this.applications[this.editingAppId] = newApp; - this.editingAppId = undefined; - } else { - this.applications.push(newApp); - } + const isEdit = this.editingAppId !== undefined; + const url = isEdit ? `${this.apiBase}/applications/${this.editingAppId}` : `${this.apiBase}/applications`; + const method = isEdit ? 'PUT' : 'POST'; - this.saveToStorage('applications', this.applications); - this.closeModals(); - location.href = './applications.html'; + fetch(url, { + method, + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }).then(res => res.json()).then(data => { + if (data.success) { + this.editingAppId = undefined; + alert(isEdit ? 'Application updated successfully' : 'Application created successfully'); + this.closeModals(); + location.href = './applications.html'; + } else { + alert(data.message || 'Save application failed'); + } + }).catch(err => { + console.error(err); + alert('Save application failed'); + }); } openAccountModal() { - this.editingAccountId = undefined; - document.getElementById('accountService').value = ''; - document.getElementById('accountOwner').value = ''; - document.getElementById('accountUsername').value = ''; - document.getElementById('accountPassword').value = ''; - document.getElementById('accountModal').classList.remove('hidden'); + // Refresh service options so newly added applications appear + const serviceSelect = document.getElementById('accountService'); + if (serviceSelect) { + serviceSelect.innerHTML = `` + + this.applications.map(app => ``).join(''); + } + + if (this.editingAccountId === undefined) { + const form = document.getElementById('accountForm'); + if (form) { + const serviceSelect = form.querySelector('#accountService'); + const ownerInput = form.querySelector('#accountOwner'); + const userInput = form.querySelector('#accountUsername'); + const passInput = form.querySelector('#accountPassword'); + if (serviceSelect) serviceSelect.value = ''; + if (ownerInput) ownerInput.value = this.currentUser?.Username || this.currentUser?.username || ''; + if (userInput) userInput.value = ''; + if (passInput) passInput.value = ''; + } + } + document.getElementById('accountModal').classList.add('open'); } openAppModal() { - this.editingAppId = undefined; - document.getElementById('appName').value = ''; - document.getElementById('appType').value = ''; - document.getElementById('appStatus').value = 'online'; - document.getElementById('appModal').classList.remove('hidden'); + if (this.editingAppId === undefined) { + document.getElementById('appName').value = ''; + document.getElementById('appType').value = ''; + document.getElementById('appStatus').value = 'online'; + const desc = document.getElementById('appDescription'); + const url = document.getElementById('appUrl'); + if (desc) desc.value = ''; + if (url) url.value = ''; + } + document.getElementById('appModal').classList.add('open'); } closeModals() { document.querySelectorAll('.modal-backdrop').forEach(modal => { - modal.classList.add('hidden'); + modal.classList.remove('open'); }); } @@ -538,6 +792,37 @@ class AccountManager { } } +// Global modal close functions +function closeAllModals() { + document.querySelectorAll('.modal-backdrop').forEach(modal => { + modal.classList.remove('open'); + }); +} + +function closeAccountModal() { + document.getElementById('accountModal').classList.remove('open'); +} + +function closeViewAccountModal() { + document.getElementById('viewAccountModal').classList.remove('open'); +} + +function closeDeleteAccountModal() { + document.getElementById('deleteAccountModal').classList.remove('open'); +} + +function closeAppModal() { + document.getElementById('appModal').classList.remove('open'); +} + +function closeViewAppModal() { + document.getElementById('viewAppModal').classList.remove('open'); +} + +function closeDeleteAppModal() { + document.getElementById('deleteAppModal').classList.remove('open'); +} + // Initialize app when DOM is ready let app; document.addEventListener('DOMContentLoaded', () => { diff --git a/modals.html b/modals.html new file mode 100644 index 0000000..5359426 --- /dev/null +++ b/modals.html @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + diff --git a/pages/accounts.html b/pages/accounts.html index 61cbf6a..40284df 100644 --- a/pages/accounts.html +++ b/pages/accounts.html @@ -135,15 +135,111 @@
+
+ + + + + + + + +