mirror of
https://github.com/marcogll/ap_pos.git
synced 2026-01-13 13:15:16 +00:00
Updated ReadMe
This commit is contained in:
7
ap-pos/README.md
Normal file
7
ap-pos/README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# AP-POS WebApp
|
||||||
|
|
||||||
|
Este es un sistema de punto de venta simple basado en la web.
|
||||||
|
|
||||||
|
## Futuras Implementaciones
|
||||||
|
|
||||||
|
Se tiene la intención de que esta aplicación se pueda ejecutar en un contenedor de Docker. Además, se buscará que la aplicación tenga la capacidad de interactuar con una impresora de tickets conectada vía USB en un entorno de macOS.
|
||||||
@@ -29,6 +29,7 @@ const btnTestTicket = document.getElementById('btnTestTicket');
|
|||||||
const formClient = document.getElementById('formClient');
|
const formClient = document.getElementById('formClient');
|
||||||
const tblClientsBody = document.getElementById('tblClients')?.querySelector('tbody');
|
const tblClientsBody = document.getElementById('tblClients')?.querySelector('tbody');
|
||||||
const clientDatalist = document.getElementById('client-list');
|
const clientDatalist = document.getElementById('client-list');
|
||||||
|
const formCredentials = document.getElementById('formCredentials');
|
||||||
|
|
||||||
// --- LÓGICA DE NEGOCIO ---
|
// --- LÓGICA DE NEGOCIO ---
|
||||||
|
|
||||||
@@ -61,9 +62,9 @@ async function loadDashboardData() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (incomeChart) {
|
if (incomeChart) {
|
||||||
incomeChart.data = chartData;
|
incomeChart.destroy();
|
||||||
incomeChart.update();
|
}
|
||||||
} else {
|
|
||||||
incomeChart = new Chart(ctx, {
|
incomeChart = new Chart(ctx, {
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
data: chartData,
|
data: chartData,
|
||||||
@@ -72,7 +73,6 @@ async function loadDashboardData() {
|
|||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading dashboard:', error);
|
console.error('Error loading dashboard:', error);
|
||||||
}
|
}
|
||||||
@@ -254,6 +254,35 @@ async function handleSaveSettings(e) {
|
|||||||
alert('Configuración guardada.');
|
alert('Configuración guardada.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleSaveCredentials(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const username = document.getElementById('s-username').value;
|
||||||
|
const password = document.getElementById('s-password').value;
|
||||||
|
|
||||||
|
const body = { username };
|
||||||
|
if (password) {
|
||||||
|
body.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/user', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(body)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
alert('Credenciales actualizadas.');
|
||||||
|
document.getElementById('s-password').value = '';
|
||||||
|
} else {
|
||||||
|
const error = await response.json();
|
||||||
|
alert(`Error: ${error.error}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert('Error de conexión al guardar credenciales.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleNewMovement(e) {
|
async function handleNewMovement(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const form = e.target;
|
const form = e.target;
|
||||||
@@ -393,6 +422,7 @@ async function initializeApp() {
|
|||||||
const btnLogout = document.getElementById('btnLogout');
|
const btnLogout = document.getElementById('btnLogout');
|
||||||
|
|
||||||
formSettings?.addEventListener('submit', handleSaveSettings);
|
formSettings?.addEventListener('submit', handleSaveSettings);
|
||||||
|
formCredentials?.addEventListener('submit', handleSaveCredentials);
|
||||||
formMove?.addEventListener('submit', handleNewMovement);
|
formMove?.addEventListener('submit', handleNewMovement);
|
||||||
tblMovesBody?.addEventListener('click', handleTableClick);
|
tblMovesBody?.addEventListener('click', handleTableClick);
|
||||||
tblClientsBody?.addEventListener('click', handleTableClick);
|
tblClientsBody?.addEventListener('click', handleTableClick);
|
||||||
@@ -414,13 +444,17 @@ async function initializeApp() {
|
|||||||
Promise.all([
|
Promise.all([
|
||||||
load(KEY_SETTINGS, DEFAULT_SETTINGS),
|
load(KEY_SETTINGS, DEFAULT_SETTINGS),
|
||||||
load(KEY_DATA, []),
|
load(KEY_DATA, []),
|
||||||
load(KEY_CLIENTS, [])
|
load(KEY_CLIENTS, []),
|
||||||
|
fetch('/api/user').then(res => res.json())
|
||||||
]).then(values => {
|
]).then(values => {
|
||||||
[settings, movements, clients] = values;
|
[settings, movements, clients, user] = values;
|
||||||
renderSettings();
|
renderSettings();
|
||||||
renderTable();
|
renderTable();
|
||||||
renderClientsTable();
|
renderClientsTable();
|
||||||
updateClientDatalist();
|
updateClientDatalist();
|
||||||
|
if (user) {
|
||||||
|
document.getElementById('s-username').value = user.username;
|
||||||
|
}
|
||||||
// Cargar datos del dashboard al inicio
|
// Cargar datos del dashboard al inicio
|
||||||
loadDashboardData();
|
loadDashboardData();
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
|||||||
@@ -178,6 +178,20 @@
|
|||||||
Toda la información de tu negocio (clientes, recibos y configuración) se guarda de forma segura en el archivo <strong>ap-pos.db</strong>, ubicado en la misma carpeta que la aplicación. Para hacer un respaldo, simplemente copia este archivo.
|
Toda la información de tu negocio (clientes, recibos y configuración) se guarda de forma segura en el archivo <strong>ap-pos.db</strong>, ubicado en la misma carpeta que la aplicación. Para hacer un respaldo, simplemente copia este archivo.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="section">
|
||||||
|
<h2>Credenciales de Acceso</h2>
|
||||||
|
<form id="formCredentials">
|
||||||
|
<div class="form-grid">
|
||||||
|
<label>Usuario:</label>
|
||||||
|
<input type="text" id="s-username" required />
|
||||||
|
<label>Nueva Contraseña:</label>
|
||||||
|
<input type="password" id="s-password" placeholder="Dejar en blanco para no cambiar" />
|
||||||
|
</div>
|
||||||
|
<div class="form-actions">
|
||||||
|
<button type="submit">Guardar Credenciales</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
@@ -233,6 +233,47 @@ apiRouter.delete('/movements/:id', (req, res) => {
|
|||||||
// Registrar el router de la API protegida
|
// Registrar el router de la API protegida
|
||||||
app.use('/api', apiRouter);
|
app.use('/api', apiRouter);
|
||||||
|
|
||||||
|
// --- User Management ---
|
||||||
|
apiRouter.get('/user', (req, res) => {
|
||||||
|
db.get("SELECT id, username FROM users WHERE id = ?", [req.session.userId], (err, row) => {
|
||||||
|
if (err) {
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.json(row);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
apiRouter.post('/user', (req, res) => {
|
||||||
|
const { username, password } = req.body;
|
||||||
|
if (!username) {
|
||||||
|
return res.status(400).json({ error: 'Username is required' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password) {
|
||||||
|
// Si se proporciona una nueva contraseña, hashearla y actualizar todo
|
||||||
|
bcrypt.hash(password, SALT_ROUNDS, (err, hash) => {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: 'Error hashing password' });
|
||||||
|
}
|
||||||
|
db.run('UPDATE users SET username = ?, password = ? WHERE id = ?', [username, hash, req.session.userId], function(err) {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
res.json({ message: 'User credentials updated successfully' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Si no se proporciona contraseña, solo actualizar el nombre de usuario
|
||||||
|
db.run('UPDATE users SET username = ? WHERE id = ?', [username, req.session.userId], function(err) {
|
||||||
|
if (err) {
|
||||||
|
return res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
res.json({ message: 'Username updated successfully' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// --- Dashboard Route ---
|
// --- Dashboard Route ---
|
||||||
apiRouter.get('/dashboard', (req, res) => {
|
apiRouter.get('/dashboard', (req, res) => {
|
||||||
const queries = {
|
const queries = {
|
||||||
|
|||||||
Reference in New Issue
Block a user