diff --git a/server/Dockerfile b/server/Dockerfile
index 6762e55..e69de29 100644
--- a/server/Dockerfile
+++ b/server/Dockerfile
@@ -1,7 +0,0 @@
-FROM node:18-alpine
-WORKDIR /app
-COPY package*.json ./
-RUN npm install --production
-COPY . .
-EXPOSE 3001
-CMD ["node", "server.js"]
diff --git a/server/Dockerfile.txt b/server/Dockerfile.txt
index 21b924d..ca4eef1 100644
--- a/server/Dockerfile.txt
+++ b/server/Dockerfile.txt
@@ -1,8 +1,14 @@
FROM node:18-alpine
WORKDIR /app
+
+# Set production environment
+ENV NODE_ENV=production
+
COPY package*.json ./
RUN npm install --production
+
COPY . .
+
EXPOSE 3001
CMD ["node", "server.js"]
diff --git a/server/db.js b/server/db.js
index 24790ed..72656af 100644
--- a/server/db.js
+++ b/server/db.js
@@ -322,7 +322,8 @@ const initDb = async () => {
multiCondo: true,
tickets: true,
payPal: true,
- notices: true
+ notices: true,
+ reports: true
};
if (rows.length === 0) {
diff --git a/server/server.js b/server/server.js
index 39c0ecd..bb8d243 100644
--- a/server/server.js
+++ b/server/server.js
@@ -148,7 +148,7 @@ app.get('/api/settings', authenticateToken, async (req, res) => {
res.json({
currentYear: rows[0].current_year,
smtpConfig: rows[0].smtp_config || {},
- features: rows[0].features || { multiCondo: true, tickets: true, payPal: true, notices: true }
+ features: rows[0].features || { multiCondo: true, tickets: true, payPal: true, notices: true, reports: true }
});
} else { res.status(404).json({ message: 'Settings not found' }); }
} catch (e) { res.status(500).json({ error: e.message }); }
@@ -349,7 +349,7 @@ app.get('/api/notices/unread', authenticateToken, async (req, res) => {
// --- PAYMENTS ---
app.get('/api/payments', authenticateToken, async (req, res) => {
- const { familyId } = req.query;
+ const { familyId, condoId } = req.query;
try {
const isPrivileged = req.user.role === 'admin' || req.user.role === 'poweruser';
if (!isPrivileged) {
@@ -359,12 +359,27 @@ app.get('/api/payments', authenticateToken, async (req, res) => {
return res.json(rows.map(mapPaymentRow));
}
}
- let query = 'SELECT * FROM payments';
+
+ let query = 'SELECT p.* FROM payments p';
let params = [];
- if (familyId) {
- query += ' WHERE family_id = ?';
- params.push(familyId);
+
+ // If condoId provided, we need to JOIN with families to filter
+ if (condoId) {
+ query += ' JOIN families f ON p.family_id = f.id WHERE f.condo_id = ?';
+ params.push(condoId);
+
+ if (familyId) {
+ query += ' AND p.family_id = ?';
+ params.push(familyId);
+ }
+ } else if (familyId) {
+ query += ' WHERE p.family_id = ?';
+ params.push(familyId);
}
+
+ // Sort by date (newest first)
+ query += ' ORDER BY p.date_paid DESC';
+
const [rows] = await pool.query(query, params);
res.json(rows.map(mapPaymentRow));
} catch (e) { res.status(500).json({ error: e.message }); }
diff --git a/services/mockDb.ts b/services/mockDb.ts
index c64af97..f8213a0 100644
--- a/services/mockDb.ts
+++ b/services/mockDb.ts
@@ -226,6 +226,10 @@ export const CondoService = {
return request
(`/payments?familyId=${familyId}`);
},
+ getCondoPayments: async (condoId: string): Promise => {
+ return request(`/payments?condoId=${condoId}`);
+ },
+
addPayment: async (payment: Omit): Promise => {
return request('/payments', {
method: 'POST',
diff --git a/types.ts b/types.ts
index 7796c8d..3a5f9e4 100644
--- a/types.ts
+++ b/types.ts
@@ -51,6 +51,7 @@ export interface AppFeatures {
tickets: boolean;
payPal: boolean;
notices: boolean;
+ reports: boolean;
}
export interface AlertDefinition {
@@ -164,4 +165,4 @@ export interface Ticket {
attachments?: TicketAttachment[];
userName?: string; // Joined field
userEmail?: string; // Joined field
-}
\ No newline at end of file
+}