done manager account
This commit is contained in:
223
public/js/app.js
223
public/js/app.js
@@ -72,11 +72,50 @@ class AccountManager {
|
||||
await this.fetchAccounts();
|
||||
this.setupEventListeners();
|
||||
this.loadModals(); // Load modals từ file riêng
|
||||
// Render dashboard content (only for index.html)
|
||||
// Single-page navigation based on hash
|
||||
this.handleRoute(location.hash || '#dashboard');
|
||||
window.addEventListener('hashchange', () => this.handleRoute(location.hash));
|
||||
}
|
||||
|
||||
handleRoute(hash) {
|
||||
const route = (hash || '#dashboard').replace('#', '') || 'dashboard';
|
||||
this.renderView(route);
|
||||
}
|
||||
|
||||
renderView(page) {
|
||||
this.currentPage = page;
|
||||
const mainContent = document.getElementById('mainContent');
|
||||
if (mainContent && window.location.pathname.endsWith('index.html')) {
|
||||
if (!mainContent) return;
|
||||
|
||||
if (page === 'applications') {
|
||||
mainContent.innerHTML = this.getApplicationsContent();
|
||||
this.setupAccountRowListeners();
|
||||
this.setupAddButtonListeners();
|
||||
this.setupFilters();
|
||||
} else if (page === 'accounts') {
|
||||
mainContent.innerHTML = this.getAccountsContent();
|
||||
this.setupAccountRowListeners();
|
||||
this.setupAddButtonListeners();
|
||||
this.setupFilters();
|
||||
} else {
|
||||
mainContent.innerHTML = this.renderDashboard();
|
||||
}
|
||||
|
||||
this.restoreSearchFocus();
|
||||
this.setActiveNav(page);
|
||||
}
|
||||
|
||||
setActiveNav(page) {
|
||||
document.querySelectorAll('[data-nav]').forEach(link => {
|
||||
const isActive = link.dataset.nav === page;
|
||||
link.classList.toggle('border-l-4', isActive);
|
||||
link.classList.toggle('border-blue-600', isActive);
|
||||
link.classList.toggle('bg-slate-200/80', isActive);
|
||||
link.classList.toggle('dark:bg-slate-800', isActive);
|
||||
link.classList.toggle('text-slate-900', isActive);
|
||||
link.classList.toggle('dark:text-slate-50', isActive);
|
||||
link.classList.toggle('font-bold', isActive);
|
||||
});
|
||||
}
|
||||
|
||||
async fetchApplications() {
|
||||
@@ -103,22 +142,47 @@ class AccountManager {
|
||||
|
||||
async loadModals() {
|
||||
try {
|
||||
const existingContainer = document.getElementById('modalsContainer');
|
||||
if (existingContainer && existingContainer.children.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
const container = existingContainer || document.createElement('div');
|
||||
if (!container.id) container.id = 'modalsContainer';
|
||||
container.innerHTML = modalsHTML;
|
||||
if (!container.parentElement) {
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
|
||||
this.setupFormListeners();
|
||||
this.setupAccountRowListeners();
|
||||
this.setupAddButtonListeners();
|
||||
this.setupFilters();
|
||||
} catch (error) {
|
||||
console.error('Lỗi load modals:', error);
|
||||
}
|
||||
}
|
||||
|
||||
restoreSearchFocus() {
|
||||
const accountSearch = document.getElementById('accountSearch');
|
||||
const appSearch = document.getElementById('appSearch');
|
||||
|
||||
if (accountSearch && accountSearch.dataset.focused === 'true') {
|
||||
const pos = accountSearch.selectionStart || accountSearch.value.length;
|
||||
accountSearch.focus();
|
||||
accountSearch.setSelectionRange(pos, pos);
|
||||
}
|
||||
|
||||
if (appSearch && appSearch.dataset.focused === 'true') {
|
||||
const pos = appSearch.selectionStart || appSearch.value.length;
|
||||
appSearch.focus();
|
||||
appSearch.setSelectionRange(pos, pos);
|
||||
}
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
// Modal close buttons
|
||||
document.querySelectorAll('[data-close-modal]').forEach(btn => {
|
||||
@@ -341,7 +405,7 @@ class AccountManager {
|
||||
<th class="px-4 py-2.5 text-[10px] font-bold uppercase tracking-wider text-slate-500 text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
<tbody class="divide-y divide-slate-100 accounts-table-body">
|
||||
${filteredAccounts.map(acc => `
|
||||
<tr class="hover:bg-slate-50/80 transition-colors group account-row" data-account-id="${acc.AccountId}">
|
||||
<td class="px-4 py-3 text-sm font-medium text-slate-900">${acc.Email || '-'}</td>
|
||||
@@ -447,7 +511,7 @@ class AccountManager {
|
||||
<th class="px-6 py-2.5 text-[10px] font-bold text-on-surface-variant uppercase tracking-widest text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-outline-variant/5">
|
||||
<tbody class="divide-y divide-outline-variant/5 apps-table-body">
|
||||
${filteredApps.map(app => `
|
||||
<tr class="hover:bg-surface-container-low/30 transition-colors group">
|
||||
<td class="px-6 py-3">
|
||||
@@ -493,6 +557,76 @@ class AccountManager {
|
||||
`;
|
||||
}
|
||||
|
||||
renderAccountsTableBody() {
|
||||
const tbody = document.querySelector('.accounts-table-body');
|
||||
if (!tbody) return;
|
||||
const filteredAccounts = this.getFilteredAccounts();
|
||||
tbody.innerHTML = filteredAccounts.map(acc => `
|
||||
<tr class="hover:bg-slate-50/80 transition-colors group account-row" data-account-id="${acc.AccountId}">
|
||||
<td class="px-4 py-3 text-sm font-medium text-slate-900">${acc.Email || '-'}</td>
|
||||
<td class="px-4 py-3 text-sm text-slate-600">${acc.AccountUsername || '-'}</td>
|
||||
<td class="px-4 py-3 text-sm">
|
||||
<span class="px-2 py-1 bg-blue-100 text-blue-700 rounded text-xs font-semibold">${acc.AppName || '-'}</span>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<button class="p-1.5 text-slate-400 hover:text-slate-600 transition-colors view-account" data-account-id="${acc.AccountId}" title="View Details">
|
||||
<span class="material-symbols-outlined text-lg">info</span>
|
||||
</button>
|
||||
<button class="p-1.5 text-slate-400 hover:text-primary transition-colors edit-account" data-account-id="${acc.AccountId}">
|
||||
<span class="material-symbols-outlined text-lg">edit</span>
|
||||
</button>
|
||||
<button class="p-1.5 text-slate-400 hover:text-error transition-colors delete-account" data-account-id="${acc.AccountId}">
|
||||
<span class="material-symbols-outlined text-lg">delete</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
this.setupAccountRowListeners();
|
||||
}
|
||||
|
||||
renderApplicationsTableBody() {
|
||||
const tbody = document.querySelector('.apps-table-body');
|
||||
if (!tbody) return;
|
||||
const filteredApps = this.getFilteredApplications();
|
||||
tbody.innerHTML = filteredApps.map(app => `
|
||||
<tr class="hover:bg-surface-container-low/30 transition-colors group">
|
||||
<td class="px-6 py-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="w-8 h-8 rounded bg-surface-container-highest flex items-center justify-center text-primary">
|
||||
<span class="material-symbols-outlined text-sm">${app.Icon || 'apps'}</span>
|
||||
</div>
|
||||
<span class="font-bold text-sm text-on-surface">${app.Name}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-3">
|
||||
<span class="px-2 py-0.5 rounded-full text-[9px] font-black uppercase bg-surface-container-highest text-on-surface-variant">${app.Type}</span>
|
||||
</td>
|
||||
<td class="px-6 py-3 text-sm text-on-surface-variant max-w-xs truncate" title="${app.Description || ''}">${app.Description || '-'}</td>
|
||||
<td class="px-6 py-3 text-sm text-primary max-w-xs truncate">${(app.Url || app.url) ? `<a href="${app.Url || app.url}" target="_blank" class="underline">${app.Url || app.url}</a>` : '-'}</td>
|
||||
<td class="px-6 py-3">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<div class="w-1.5 h-1.5 rounded-full ${(app.Status || app.status) === 'online' ? 'bg-primary' : 'bg-error'} ring-2 ${(app.Status || app.status) === 'online' ? 'ring-primary/20' : 'ring-error/20'}"></div>
|
||||
<span class="text-xs font-medium ${(app.Status || app.status) === 'online' ? 'text-on-primary-fixed-variant' : 'text-error'}">${(app.Status || app.status) === 'online' ? 'Online' : 'Offline'}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-3 text-right">
|
||||
<div class="flex items-center justify-end gap-1">
|
||||
<button class="p-1.5 text-on-surface-variant hover:text-on-surface transition-colors view-app" data-app-id="${app.AppId}" title="View Details">
|
||||
<span class="material-symbols-outlined text-lg">info</span>
|
||||
</button>
|
||||
<button class="p-1.5 text-on-surface-variant hover:text-primary transition-colors edit-app" data-app-id="${app.AppId}">
|
||||
<span class="material-symbols-outlined text-lg">edit</span>
|
||||
</button>
|
||||
<button class="p-1.5 text-on-surface-variant hover:text-error transition-colors delete-app" data-app-id="${app.AppId}">
|
||||
<span class="material-symbols-outlined text-lg">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
this.setupAccountRowListeners();
|
||||
}
|
||||
|
||||
setupAccountRowListeners() {
|
||||
// View Account listeners
|
||||
document.querySelectorAll('.view-account').forEach(btn => {
|
||||
@@ -504,8 +638,10 @@ class AccountManager {
|
||||
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';
|
||||
const passwordEl = document.getElementById('viewAccountPassword');
|
||||
passwordEl.dataset.password = account?.AccountPassword || '';
|
||||
passwordEl.textContent = '••••••••';
|
||||
passwordEl.dataset.visible = 'false';
|
||||
document.getElementById('toggleIcon').textContent = 'visibility';
|
||||
document.getElementById('viewAccountModal').classList.add('open');
|
||||
});
|
||||
@@ -596,14 +732,15 @@ class AccountManager {
|
||||
btn.addEventListener('click', () => {
|
||||
const passwordEl = document.getElementById('viewAccountPassword');
|
||||
const toggleIcon = document.getElementById('toggleIcon');
|
||||
const storedPwd = passwordEl.dataset.password || this.currentViewAccount?.AccountPassword || '';
|
||||
const isVisible = passwordEl.dataset.visible === 'true';
|
||||
|
||||
|
||||
if (isVisible) {
|
||||
passwordEl.textContent = '••••••••';
|
||||
passwordEl.dataset.visible = 'false';
|
||||
toggleIcon.textContent = 'visibility';
|
||||
} else {
|
||||
passwordEl.textContent = this.currentViewAccount?.AccountPassword || '';
|
||||
passwordEl.textContent = storedPwd || '(no password stored)';
|
||||
passwordEl.dataset.visible = 'true';
|
||||
toggleIcon.textContent = 'visibility_off';
|
||||
}
|
||||
@@ -733,26 +870,46 @@ class AccountManager {
|
||||
serviceFilter.value = this.accountServiceFilter || '';
|
||||
serviceFilter.addEventListener('change', (e) => {
|
||||
this.accountServiceFilter = e.target.value;
|
||||
this.refreshAccountsUI();
|
||||
this.renderAccountsTableBody();
|
||||
});
|
||||
}
|
||||
|
||||
const accountSearch = document.getElementById('accountSearch');
|
||||
if (accountSearch) {
|
||||
accountSearch.value = this.accountSearchTerm;
|
||||
accountSearch.addEventListener('input', (e) => {
|
||||
this.accountSearchTerm = e.target.value.toLowerCase();
|
||||
this.refreshAccountsUI();
|
||||
});
|
||||
const handleAccountSearch = event => {
|
||||
this.accountSearchTerm = event.target.value.toLowerCase();
|
||||
this.renderAccountsTableBody();
|
||||
};
|
||||
|
||||
accountSearch.addEventListener('input', handleAccountSearch);
|
||||
|
||||
// Restore focus/selection after renders to avoid typing interruptions
|
||||
accountSearch.addEventListener('focus', () => {
|
||||
accountSearch.dataset.focused = 'true';
|
||||
});
|
||||
accountSearch.addEventListener('blur', () => {
|
||||
accountSearch.dataset.focused = 'false';
|
||||
});
|
||||
}
|
||||
|
||||
const appSearch = document.getElementById('appSearch');
|
||||
if (appSearch) {
|
||||
appSearch.value = this.applicationSearchTerm;
|
||||
appSearch.addEventListener('input', (e) => {
|
||||
this.applicationSearchTerm = e.target.value.toLowerCase();
|
||||
this.refreshApplicationsUI();
|
||||
});
|
||||
const handleApplicationSearch = event => {
|
||||
this.applicationSearchTerm = event.target.value.toLowerCase();
|
||||
this.renderApplicationsTableBody();
|
||||
};
|
||||
|
||||
appSearch.addEventListener('input', handleApplicationSearch);
|
||||
|
||||
// Restore focus/selection after renders to avoid typing interruptions
|
||||
appSearch.addEventListener('focus', () => {
|
||||
appSearch.dataset.focused = 'true';
|
||||
});
|
||||
appSearch.addEventListener('blur', () => {
|
||||
appSearch.dataset.focused = 'false';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -820,12 +977,8 @@ class AccountManager {
|
||||
|
||||
async refreshAccountsUI() {
|
||||
await this.fetchAccounts();
|
||||
const mainContent = document.getElementById('mainContent');
|
||||
if (mainContent) {
|
||||
mainContent.innerHTML = this.getAccountsContent();
|
||||
this.setupAccountRowListeners();
|
||||
this.setupAddButtonListeners();
|
||||
this.setupFilters();
|
||||
if (this.currentPage === 'accounts') {
|
||||
this.renderView('accounts');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -865,12 +1018,8 @@ class AccountManager {
|
||||
|
||||
async refreshApplicationsUI() {
|
||||
await this.fetchApplications();
|
||||
const mainContent = document.getElementById('mainContent');
|
||||
if (mainContent) {
|
||||
mainContent.innerHTML = this.getApplicationsContent();
|
||||
this.setupAccountRowListeners();
|
||||
this.setupAddButtonListeners();
|
||||
this.setupFilters();
|
||||
if (this.currentPage === 'applications') {
|
||||
this.renderView('applications');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,163 +90,9 @@
|
||||
.modal-backdrop.open .modal-content { transform: scale(1); }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-background text-on-surface antialiased flex h-screen w-screen">
|
||||
<aside class="h-screen w-56 flex flex-col bg-slate-100 dark:bg-slate-900 font-manrope text-sm font-medium border-r border-outline-variant/10 shrink-0">
|
||||
<div class="flex flex-col h-full py-6">
|
||||
<div class="px-6 mb-8">
|
||||
<div class="text-lg font-black text-slate-900 dark:text-slate-50 tracking-tight leading-none">Robot Account</div>
|
||||
<div class="text-[10px] uppercase tracking-widest text-on-surface-variant mt-1.5 font-bold">Admin Console</div>
|
||||
</div>
|
||||
<nav class="flex-1 px-3 space-y-1">
|
||||
<a href="./index.html" 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">
|
||||
<span class="material-symbols-outlined">dashboard</span>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
<a href="./applications.html" 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">
|
||||
<span class="material-symbols-outlined">apps</span>
|
||||
<span>Applications</span>
|
||||
</a>
|
||||
<a href="./accounts.html" 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">
|
||||
<span class="material-symbols-outlined">manage_accounts</span>
|
||||
<span>Accounts</span>
|
||||
</a>
|
||||
</nav>
|
||||
<div class="px-6 pt-4 border-t border-outline-variant/10">
|
||||
<div class="text-[10px] font-bold text-on-surface-variant/40 uppercase tracking-widest">v1.0.0</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
<main class="flex-1 flex flex-col h-screen min-w-0">
|
||||
<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">
|
||||
<div class="flex items-center bg-surface-container-high px-3 py-1.5 rounded-full w-64 group focus-within:ring-2 ring-primary/20 transition-all">
|
||||
<span class="material-symbols-outlined text-on-surface-variant text-base">search</span>
|
||||
<input id="searchInput" class="bg-transparent border-none focus:ring-0 text-xs w-full placeholder:text-on-surface-variant/60 py-0" placeholder="Search resources..." type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2 px-4 py-2 rounded-lg bg-slate-100 dark:bg-slate-800">
|
||||
<span class="material-symbols-outlined text-slate-600 dark:text-slate-400">account_circle</span>
|
||||
<div class="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>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<span class="material-symbols-outlined">logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<div id="mainContent" class="flex-1 overflow-hidden"></div>
|
||||
<div id="modalsContainer">
|
||||
<!-- Add Account Modal -->
|
||||
<div class="modal-backdrop fixed inset-0 z-[100] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="accountModal">
|
||||
<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">Add New Account</h3>
|
||||
<button class="p-1.5 rounded-lg hover:bg-slate-200 text-slate-400 transition-colors" onclick="closeAccountModal()">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<form id="accountForm" class="p-6 space-y-4">
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Service</label>
|
||||
<select id="accountService" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" required>
|
||||
<option value="">Select a service</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Owner Name</label>
|
||||
<input type="text" id="accountOwner" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" required placeholder="John Doe">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Username</label>
|
||||
<input type="text" id="accountUsername" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" required placeholder="username">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Password</label>
|
||||
<input type="password" id="accountPassword" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" required placeholder="••••••••">
|
||||
</div>
|
||||
<div class="flex gap-3 pt-4">
|
||||
<button type="button" class="flex-1 px-4 py-2 text-xs font-bold text-slate-600 border border-slate-200 rounded-lg" onclick="closeAccountModal()">Cancel</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">Save Account</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- View Account Modal -->
|
||||
<div class="modal-backdrop fixed inset-0 z-[100] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="viewAccountModal">
|
||||
<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">Account Details</h3>
|
||||
<button class="p-1.5 rounded-lg hover:bg-slate-200 text-slate-400 transition-colors" onclick="closeViewAccountModal()">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-6 space-y-4">
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Service</label>
|
||||
<div id="viewAccountService" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50 text-slate-600">-</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Owner Name</label>
|
||||
<div id="viewAccountOwner" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50 text-slate-600">-</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Username</label>
|
||||
<div id="viewAccountUsername" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50 text-slate-600">-</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Password</label>
|
||||
<div class="flex items-center gap-2">
|
||||
<div id="viewAccountPassword" class="flex-1 border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50 text-slate-600">••••••••</div>
|
||||
<button type="button" class="p-2 rounded-lg hover:bg-slate-100 text-slate-400 transition-colors toggle-password">
|
||||
<span class="material-symbols-outlined text-lg" id="toggleIcon">visibility</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3 pt-4">
|
||||
<button type="button" class="flex-1 px-4 py-2 text-xs font-bold text-slate-600 border border-slate-200 rounded-lg" onclick="closeViewAccountModal()">Close</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-account-from-view">Edit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete Account Modal -->
|
||||
<div class="modal-backdrop fixed inset-0 z-[100] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="deleteAccountModal">
|
||||
<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">Delete Account</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<p class="text-sm text-slate-600 mb-6">Are you sure you want to delete the account for <strong id="deleteAccountUsername">-</strong>? This action cannot be undone.</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="closeDeleteAccountModal()">Cancel</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-account">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<script src="../js/app.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
if (app) {
|
||||
const mainContent = document.getElementById('mainContent');
|
||||
const modalsContainer = document.getElementById('modalsContainer');
|
||||
await app.initPromise;
|
||||
if (mainContent) {
|
||||
mainContent.innerHTML = app.getAccountsContent();
|
||||
app.setupAccountRowListeners();
|
||||
app.setupAddButtonListeners();
|
||||
app.setupFilters();
|
||||
}
|
||||
}
|
||||
});
|
||||
window.location.href = './index.html#accounts';
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -90,150 +90,9 @@
|
||||
.modal-backdrop.open .modal-content { transform: scale(1); }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-background text-on-surface antialiased flex h-screen w-screen">
|
||||
<aside class="h-screen w-56 flex flex-col bg-slate-100 dark:bg-slate-900 font-manrope text-sm font-medium border-r border-outline-variant/10 shrink-0">
|
||||
<div class="flex flex-col h-full py-6">
|
||||
<div class="px-6 mb-8">
|
||||
<div class="text-lg font-black text-slate-900 dark:text-slate-50 tracking-tight leading-none">Robot Account</div>
|
||||
<div class="text-[10px] uppercase tracking-widest text-on-surface-variant mt-1.5 font-bold">Admin Console</div>
|
||||
</div>
|
||||
<nav class="flex-1 px-3 space-y-1">
|
||||
<a href="./index.html" 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">
|
||||
<span class="material-symbols-outlined">dashboard</span>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
<a href="./applications.html" 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">
|
||||
<span class="material-symbols-outlined">apps</span>
|
||||
<span>Applications</span>
|
||||
</a>
|
||||
<a href="./accounts.html" 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">
|
||||
<span class="material-symbols-outlined">manage_accounts</span>
|
||||
<span>Accounts</span>
|
||||
</a>
|
||||
</nav>
|
||||
<div class="px-6 pt-4 border-t border-outline-variant/10">
|
||||
<div class="text-[10px] font-bold text-on-surface-variant/40 uppercase tracking-widest">v1.0.0</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
<main class="flex-1 flex flex-col h-screen min-w-0">
|
||||
<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">
|
||||
<div class="flex items-center bg-surface-container-high px-3 py-1.5 rounded-full w-64 group focus-within:ring-2 ring-primary/20 transition-all">
|
||||
<span class="material-symbols-outlined text-on-surface-variant text-base">search</span>
|
||||
<input id="searchInput" class="bg-transparent border-none focus:ring-0 text-xs w-full placeholder:text-on-surface-variant/60 py-0" placeholder="Search resources..." type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2 px-4 py-2 rounded-lg bg-slate-100 dark:bg-slate-800">
|
||||
<span class="material-symbols-outlined text-slate-600 dark:text-slate-400">account_circle</span>
|
||||
<div class="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>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<span class="material-symbols-outlined">logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
<div id="mainContent" class="flex-1 overflow-hidden"></div>
|
||||
<div id="modalsContainer">
|
||||
<!-- Add App Modal -->
|
||||
<div class="modal-backdrop fixed inset-0 z-[100] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="appModal">
|
||||
<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">Add New Application</h3>
|
||||
<button class="p-1.5 rounded-lg hover:bg-slate-200 text-slate-400 transition-colors" onclick="closeAppModal()">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<form id="appForm" class="p-6 space-y-4">
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Application Name</label>
|
||||
<input type="text" id="appName" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" required placeholder="AWS Services">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Type</label>
|
||||
<input type="text" id="appType" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3" required placeholder="Cloud">
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Status</label>
|
||||
<select id="appStatus" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3">
|
||||
<option value="online">Online</option>
|
||||
<option value="offline">Offline</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex gap-3 pt-4">
|
||||
<button type="button" class="flex-1 px-4 py-2 text-xs font-bold text-slate-600 border border-slate-200 rounded-lg" onclick="closeAppModal()">Cancel</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">Save Application</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- View App Modal -->
|
||||
<div class="modal-backdrop fixed inset-0 z-[100] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="viewAppModal">
|
||||
<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">Application Details</h3>
|
||||
<button class="p-1.5 rounded-lg hover:bg-slate-200 text-slate-400 transition-colors" onclick="closeViewAppModal()">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-6 space-y-4">
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Application Name</label>
|
||||
<div id="viewAppName" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50 text-slate-600">-</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Type</label>
|
||||
<div id="viewAppType" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50 text-slate-600">-</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[10px] font-bold uppercase text-slate-500 tracking-widest block mb-1">Status</label>
|
||||
<div id="viewAppStatus" class="w-full border border-slate-200 rounded-lg text-sm py-2.5 px-3 bg-slate-50 text-slate-600">-</div>
|
||||
</div>
|
||||
<div class="flex gap-3 pt-4">
|
||||
<button type="button" class="flex-1 px-4 py-2 text-xs font-bold text-slate-600 border border-slate-200 rounded-lg" onclick="closeViewAppModal()">Close</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-app-from-view">Edit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete App Modal -->
|
||||
<div class="modal-backdrop fixed inset-0 z-[100] flex items-center justify-center bg-black/40 backdrop-blur-sm" id="deleteAppModal">
|
||||
<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">Delete Application</h3>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<p class="text-sm text-slate-600 mb-6">Are you sure you want to delete <strong id="deleteAppName">-</strong>? This action cannot be undone.</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="closeDeleteAppModal()">Cancel</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-app">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<script src="../js/app.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
if (app) {
|
||||
const mainContent = document.getElementById('mainContent');
|
||||
await app.initPromise;
|
||||
if (mainContent) {
|
||||
mainContent.innerHTML = app.getApplicationsContent();
|
||||
app.setupAccountRowListeners();
|
||||
app.setupAddButtonListeners();
|
||||
app.setupFilters();
|
||||
}
|
||||
}
|
||||
});
|
||||
window.location.href = './index.html#applications';
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -124,15 +124,15 @@
|
||||
</div>
|
||||
<!-- Primary Nav -->
|
||||
<nav class="flex-1 px-3 space-y-1">
|
||||
<a href="#" onclick="location.reload(); return false;" 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">
|
||||
<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">
|
||||
<span class="material-symbols-outlined">dashboard</span>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
<a href="./applications.html" 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">
|
||||
<span class="material-symbols-outlined">apps</span>
|
||||
<span>Applications</span>
|
||||
</a>
|
||||
<a href="./accounts.html" 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">
|
||||
<span class="material-symbols-outlined">manage_accounts</span>
|
||||
<span>Accounts</span>
|
||||
</a>
|
||||
@@ -149,10 +149,7 @@
|
||||
<!-- 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">
|
||||
<div class="flex items-center bg-surface-container-high px-3 py-1.5 rounded-full w-64 group focus-within:ring-2 ring-primary/20 transition-all">
|
||||
<span class="material-symbols-outlined text-on-surface-variant text-base">search</span>
|
||||
<input id="searchInput" class="bg-transparent border-none focus:ring-0 text-xs w-full placeholder:text-on-surface-variant/60 py-0" placeholder="Search resources..." type="text"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2 px-4 py-2 rounded-lg bg-slate-100 dark:bg-slate-800">
|
||||
|
||||
Reference in New Issue
Block a user