Browse Source

update vendor, hospital and logs related to sso keycloak

pearlgw 2 months ago
parent
commit
87523a065b

+ 3 - 3
src/controllers/admin/VendorController.js

@@ -14,7 +14,7 @@ exports.getAllVendor = async (req, res) => {
14
             page, limit, search, sortBy, orderBy
14
             page, limit, search, sortBy, orderBy
15
         });
15
         });
16
 
16
 
17
-        return VendorCollection(req, res, vendors, total, page, limit, 'Vendor data successfully retrieved');
17
+        return VendorCollection({ req, res, data: vendors, total, page, limit, message: 'Vendor data successfully retrieved' });
18
     } catch (err) {
18
     } catch (err) {
19
         return errorResponse(res, err);
19
         return errorResponse(res, err);
20
     }
20
     }
@@ -23,8 +23,8 @@ exports.getAllVendor = async (req, res) => {
23
 exports.showVendor = async (req, res) => {
23
 exports.showVendor = async (req, res) => {
24
     try {
24
     try {
25
         const id = req.params.id;
25
         const id = req.params.id;
26
-        const data = await vendorService.showVendorService(id);
27
-        return VendorResource(res, data, 'Success show vendor');
26
+        const { vendor, userName } = await vendorService.showVendorService(id);
27
+        return VendorResource(res, vendor, userName, 'Success show vendor');
28
     } catch (err) {
28
     } catch (err) {
29
         return errorResponse(res, err);
29
         return errorResponse(res, err);
30
     }
30
     }

+ 2 - 18
src/repository/admin/VendorRepository.js

@@ -15,15 +15,7 @@ const VendorRepository = {
15
                 strengths: true,
15
                 strengths: true,
16
                 weaknesses: true,
16
                 weaknesses: true,
17
                 website: true,
17
                 website: true,
18
-                user: {
19
-                    select: {
20
-                        id: true,
21
-                        username: true,
22
-                        email: true,
23
-                        firstname: true,
24
-                        lastname: true,
25
-                    }
26
-                },
18
+                created_by: true,
27
                 _count: {
19
                 _count: {
28
                     select: {
20
                     select: {
29
                         vendor_histories: {
21
                         vendor_histories: {
@@ -66,15 +58,7 @@ const VendorRepository = {
66
                 strengths: true,
58
                 strengths: true,
67
                 weaknesses: true,
59
                 weaknesses: true,
68
                 website: true,
60
                 website: true,
69
-                user: {
70
-                    select: {
71
-                        id: true,
72
-                        username: true,
73
-                        email: true,
74
-                        firstname: true,
75
-                        lastname: true,
76
-                    }
77
-                },
61
+                created_by: true,
78
                 _count: {
62
                 _count: {
79
                     select: {
63
                     select: {
80
                         vendor_histories: {
64
                         vendor_histories: {

+ 19 - 15
src/resources/admin/vendor/VendorCollection.js

@@ -1,22 +1,26 @@
1
 const { ListResponse } = require("../../../utils/ListResponse");
1
 const { ListResponse } = require("../../../utils/ListResponse");
2
 const { formatISOWithoutTimezone } = require("../../../utils/FormatDate.js");
2
 const { formatISOWithoutTimezone } = require("../../../utils/FormatDate.js");
3
+const { getUserNameById } = require("../../../utils/CheckUserKeycloak");
3
 
4
 
4
-const formatItem = (item) => ({
5
-    ...item,
6
-    createdAt: formatISOWithoutTimezone(item.createdAt),
7
-    updatedAt: formatISOWithoutTimezone(item.updatedAt)
8
-});
5
+const transformVendorList = async (data = []) => {
6
+    return Promise.all(data.map(async ({ created_by, ...rest }) => {
7
+        const name = await getUserNameById(created_by);
9
 
8
 
10
-exports.VendorCollection = (req, res, data = [], total = null, page = 1, limit = 10, message = 'Success') => {
11
-    const formattedData = data.map(formatItem);
9
+        return {
10
+            ...rest,
11
+            user: {
12
+                id: created_by,
13
+                name: name
14
+            },
15
+            createdAt: formatISOWithoutTimezone(rest.createdAt),
16
+            updatedAt: formatISOWithoutTimezone(rest.updatedAt)
17
+        };
18
+    }));
19
+};
12
 
20
 
13
-    if (typeof total !== 'number') {
14
-        return res.status(200).json({
15
-            success: true,
16
-            message,
17
-            data: Array.isArray(formattedData)
18
-        });
19
-    }
21
+// Collection yang async
22
+exports.VendorCollection = async ({ req, res, data = [], total = 0, page = 1, limit = 10, message = 'Success' }) => {
23
+    const formatted = await transformVendorList(data);
20
 
24
 
21
-    return ListResponse({ req, res, data: formattedData, total, page, limit, message });
25
+    return ListResponse({ req, res, data: formatted, total, page, limit, message });
22
 };
26
 };

+ 16 - 8
src/resources/admin/vendor/VendorResource.js

@@ -1,17 +1,25 @@
1
 const { formatISOWithoutTimezone } = require("../../../utils/FormatDate");
1
 const { formatISOWithoutTimezone } = require("../../../utils/FormatDate");
2
 
2
 
3
-const formatItem = (item) => ({
4
-    ...item,
5
-    createdAt: formatISOWithoutTimezone(item.createdAt),
6
-    updatedAt: formatISOWithoutTimezone(item.updatedAt)
7
-});
3
+const formatItem = ({ item, userName }) => {
4
+    const { created_by, ...rest } = item;
8
 
5
 
9
-exports.VendorResource = (res, data, message = 'Success') => {
10
-    const formattedData = formatItem(data);
6
+    return {
7
+        ...rest,
8
+        user: {
9
+            id: created_by,
10
+            name: userName
11
+        },
12
+        createdAt: formatISOWithoutTimezone(item.createdAt),
13
+        updatedAt: formatISOWithoutTimezone(item.updatedAt)
14
+    };
15
+};
16
+
17
+exports.VendorResource = (res, item, userName, message = 'Success') => {
18
+    const formattedData = formatItem({ item, userName });
11
 
19
 
12
     return res.status(200).json({
20
     return res.status(200).json({
13
         success: true,
21
         success: true,
14
         message,
22
         message,
15
         data: formattedData
23
         data: formattedData
16
     });
24
     });
17
-};
25
+};

+ 1 - 1
src/services/admin/HospitalService.js

@@ -40,7 +40,7 @@ exports.showHospitalService = async (id) => {
40
 };
40
 };
41
 
41
 
42
 exports.storeHospitalService = async (validateData, req) => {
42
 exports.storeHospitalService = async (validateData, req) => {
43
-    const creatorId = req.user.id;
43
+    const creatorId = req.tokenData.sub;
44
     const province = await ProvinceRepository.findById(validateData.province_id);
44
     const province = await ProvinceRepository.findById(validateData.province_id);
45
     if (!province) {
45
     if (!province) {
46
         throw new HttpException('Province not found', 404);
46
         throw new HttpException('Province not found', 404);

+ 7 - 4
src/services/admin/VendorService.js

@@ -5,6 +5,7 @@ const { SearchFilter } = require('../../utils/SearchFilter.js');
5
 const timeLocal = require('../../utils/TimeLocal.js');
5
 const timeLocal = require('../../utils/TimeLocal.js');
6
 const { createLog, updateLog, deleteLog } = require('../../utils/LogActivity.js');
6
 const { createLog, updateLog, deleteLog } = require('../../utils/LogActivity.js');
7
 const { formatISOWithoutTimezone } = require('../../utils/FormatDate.js');
7
 const { formatISOWithoutTimezone } = require('../../utils/FormatDate.js');
8
+const { getUserNameById } = require('../../utils/CheckUserKeycloak.js');
8
 
9
 
9
 exports.getAllVendorService = async ({ page, limit, search, sortBy, orderBy }) => {
10
 exports.getAllVendorService = async ({ page, limit, search, sortBy, orderBy }) => {
10
     const skip = (page - 1) * limit;
11
     const skip = (page - 1) * limit;
@@ -28,11 +29,13 @@ exports.showVendorService = async (id) => {
28
         throw new HttpException("Data vendor not found", 404);
29
         throw new HttpException("Data vendor not found", 404);
29
     }
30
     }
30
 
31
 
31
-    return vendor;
32
+    const userName = await getUserNameById(vendor.created_by);
33
+
34
+    return { vendor, userName };
32
 };
35
 };
33
 
36
 
34
 exports.storeVendorService = async (validateData, req) => {
37
 exports.storeVendorService = async (validateData, req) => {
35
-    const creatorId = req.user.id;
38
+    const creatorId = req.tokenData.sub;
36
 
39
 
37
     const name_vendor = await prisma.vendor.findFirst({
40
     const name_vendor = await prisma.vendor.findFirst({
38
         where: {
41
         where: {
@@ -55,7 +58,7 @@ exports.storeVendorService = async (validateData, req) => {
55
 };
58
 };
56
 
59
 
57
 exports.updateVendorService = async (validateData, id, req) => {
60
 exports.updateVendorService = async (validateData, id, req) => {
58
-    const creatorId = req.user.id;
61
+    // const creatorId = req.user.id;
59
 
62
 
60
     const vendor = await VendorRepository.findById(id);
63
     const vendor = await VendorRepository.findById(id);
61
     if (!vendor) {
64
     if (!vendor) {
@@ -77,7 +80,7 @@ exports.updateVendorService = async (validateData, id, req) => {
77
 
80
 
78
     const payload = {
81
     const payload = {
79
         ...validateData,
82
         ...validateData,
80
-        created_by: creatorId
83
+        // created_by: creatorId
81
     };
84
     };
82
 
85
 
83
     const data = await VendorRepository.update(id, payload);
86
     const data = await VendorRepository.update(id, payload);

+ 102 - 19
src/services/sales/HospitalService.js

@@ -94,7 +94,7 @@ exports.getAllHospitalByAreaService = async ({ page, limit, search, sortBy, orde
94
 // };
94
 // };
95
 
95
 
96
 exports.storeHospitalService = async (validateData, req) => {
96
 exports.storeHospitalService = async (validateData, req) => {
97
-    const creatorId = req.user.id;
97
+    const creatorId = req.tokenData.sub;
98
 
98
 
99
     const province = await ProvinceRepository.findById(validateData.province_id);
99
     const province = await ProvinceRepository.findById(validateData.province_id);
100
     if (!province) {
100
     if (!province) {
@@ -103,7 +103,7 @@ exports.storeHospitalService = async (validateData, req) => {
103
 
103
 
104
     const userArea = await prisma.userArea.findFirst({
104
     const userArea = await prisma.userArea.findFirst({
105
         where: {
105
         where: {
106
-            user_id: req.user.id,
106
+            user_id: req.tokenData.sub,
107
             province_id: validateData.province_id
107
             province_id: validateData.province_id
108
         }
108
         }
109
     });
109
     });
@@ -135,12 +135,45 @@ exports.storeHospitalService = async (validateData, req) => {
135
 
135
 
136
     const imagePath = req.file ? `/storage/img/${req.file.filename}` : null;
136
     const imagePath = req.file ? `/storage/img/${req.file.filename}` : null;
137
 
137
 
138
+    let latitude = validateData.latitude ?? null;
139
+    let longitude = validateData.longitude ?? null;
140
+    let gmapsUrl = validateData.gmaps_url ?? null;
141
+
142
+    if (gmapsUrl) {
143
+        if (gmapsUrl.includes("www.google.com/maps")) {
144
+            const regex = /@(-?\d+\.\d+),(-?\d+\.\d+)/;
145
+            const match = gmapsUrl.match(regex);
146
+
147
+            if (match) {
148
+                latitude = parseFloat(match[1]);
149
+                longitude = parseFloat(match[2]);
150
+            } else {
151
+                throw new HttpException("Unable to extract coordinates from gmaps_url", 400);
152
+            }
153
+
154
+        } else if (gmapsUrl.includes("maps.app.goo.gl")) {
155
+            latitude = null;
156
+            longitude = null;
157
+
158
+        } else {
159
+            // URL disediakan tapi bukan dari domain yang valid
160
+            throw new HttpException("gmaps_url must be a valid Google Maps URL", 400);
161
+        }
162
+    } else if (latitude !== null && longitude !== null) {
163
+        gmapsUrl = null;
164
+    } else {
165
+        throw new HttpException("Either gmaps_url or coordinates must be provided", 400);
166
+    }
167
+
138
     const payload = {
168
     const payload = {
139
         ...validateData,
169
         ...validateData,
140
         image: imagePath,
170
         image: imagePath,
141
         progress_status: "cari_data",
171
         progress_status: "cari_data",
142
         simrs_type: "-",
172
         simrs_type: "-",
143
-        created_by: creatorId
173
+        created_by: creatorId,
174
+        latitude,
175
+        longitude,
176
+        gmaps_url: gmapsUrl,
144
     };
177
     };
145
 
178
 
146
     const data = await salesHospitalRepository.create(payload);
179
     const data = await salesHospitalRepository.create(payload);
@@ -157,7 +190,7 @@ exports.updateHospitalService = async (validateData, id, req) => {
157
 
190
 
158
     const userArea = await prisma.userArea.findFirst({
191
     const userArea = await prisma.userArea.findFirst({
159
         where: {
192
         where: {
160
-            user_id: req.user.id,
193
+            user_id: req.tokenData.sub,
161
             province_id: validateData.province_id
194
             province_id: validateData.province_id
162
         }
195
         }
163
     });
196
     });
@@ -166,14 +199,18 @@ exports.updateHospitalService = async (validateData, id, req) => {
166
         throw new HttpException("You are not authorized to update hospital in this province", 403);
199
         throw new HttpException("You are not authorized to update hospital in this province", 403);
167
     }
200
     }
168
 
201
 
169
-    const province = await ProvinceRepository.findById(validateData.province_id);
170
-    if (!province) {
171
-        throw new HttpException('Province not found', 404);
202
+    if (validateData.province_id) {
203
+        const province = await ProvinceRepository.findById(validateData.province_id);
204
+        if (!province) {
205
+            throw new HttpException('Province not found', 404);
206
+        }
172
     }
207
     }
173
 
208
 
174
-    const city = await CityRepository.findById(validateData.city_id);
175
-    if (!city) {
176
-        throw new HttpException('City not found', 404);
209
+    if (validateData.city_id) {
210
+        const city = await CityRepository.findById(validateData.city_id);
211
+        if (!city) {
212
+            throw new HttpException('City not found', 404);
213
+        }
177
     }
214
     }
178
 
215
 
179
     if (validateData.progress_status && !validProgressStatuses.includes(validateData.progress_status)) {
216
     if (validateData.progress_status && !validProgressStatuses.includes(validateData.progress_status)) {
@@ -183,16 +220,19 @@ exports.updateHospitalService = async (validateData, id, req) => {
183
         );
220
         );
184
     }
221
     }
185
 
222
 
186
-    const existingHospital = await prisma.hospital.findFirst({
187
-        where: {
188
-            name: validateData.name,
189
-            city_id: validateData.city_id,
190
-            deletedAt: null
223
+    if (validateData.name && validateData.city_id) {
224
+        const existingHospital = await prisma.hospital.findFirst({
225
+            where: {
226
+                name: validateData.name,
227
+                city_id: validateData.city_id,
228
+                deletedAt: null,
229
+                NOT: { id }
230
+            }
231
+        });
232
+
233
+        if (existingHospital) {
234
+            throw new HttpException('Hospital with same name in this city already exists', 400);
191
         }
235
         }
192
-    });
193
-
194
-    if (existingHospital) {
195
-        throw new HttpException('Hospital with same name in this city already exists', 400);
196
     }
236
     }
197
 
237
 
198
     // Jika ada file baru, replace image
238
     // Jika ada file baru, replace image
@@ -201,10 +241,53 @@ exports.updateHospitalService = async (validateData, id, req) => {
201
         imagePath = `/storage/img/${req.file.filename}`; // path relatif
241
         imagePath = `/storage/img/${req.file.filename}`; // path relatif
202
     }
242
     }
203
 
243
 
244
+    // Handle koordinat dan gmaps_url
245
+    let latitude = hospital.latitude;
246
+    let longitude = hospital.longitude;
247
+    let gmapsUrl = hospital.gmaps_url;
248
+
249
+    if (
250
+        validateData.latitude !== undefined &&
251
+        validateData.longitude !== undefined &&
252
+        validateData.latitude !== null &&
253
+        validateData.longitude !== null
254
+    ) {
255
+        // Jika diberikan lat long langsung
256
+        latitude = validateData.latitude;
257
+        longitude = validateData.longitude;
258
+        gmapsUrl = validateData.gmaps_url || gmapsUrl;
259
+    } else if (
260
+        validateData.gmaps_url &&
261
+        typeof validateData.gmaps_url === "string" &&
262
+        validateData.gmaps_url.trim() !== ""
263
+    ) {
264
+        gmapsUrl = validateData.gmaps_url;
265
+
266
+        if (gmapsUrl.includes("www.google.com/maps")) {
267
+            const regex = /@(-?\d+\.\d+),(-?\d+\.\d+)/;
268
+            const match = gmapsUrl.match(regex);
269
+            if (match) {
270
+                latitude = parseFloat(match[1]);
271
+                longitude = parseFloat(match[2]);
272
+            } else {
273
+                throw new HttpException("Unable to extract coordinates from gmaps_url", 400);
274
+            }
275
+        } else if (gmapsUrl.includes("maps.app.goo.gl")) {
276
+            // Tidak bisa ambil koordinat langsung
277
+            latitude = null;
278
+            longitude = null;
279
+        } else {
280
+            throw new HttpException("gmaps_url must be a valid Google Maps URL", 400);
281
+        }
282
+    }
283
+
204
     const payload = {
284
     const payload = {
205
         ...validateData,
285
         ...validateData,
206
         image: imagePath,
286
         image: imagePath,
207
         // created_by: req.user.id,
287
         // created_by: req.user.id,
288
+        latitude,
289
+        longitude,
290
+        gmaps_url: gmapsUrl,
208
     };
291
     };
209
 
292
 
210
     const data = await salesHospitalRepository.update(id, payload);
293
     const data = await salesHospitalRepository.update(id, payload);

+ 1 - 1
src/services/sales/VendorService.js

@@ -5,7 +5,7 @@ const { SearchFilter } = require('../../utils/SearchFilter.js');
5
 
5
 
6
 exports.getAllVendorService = async ({ page, limit, search, sortBy, orderBy }, req) => {
6
 exports.getAllVendorService = async ({ page, limit, search, sortBy, orderBy }, req) => {
7
     const skip = (page - 1) * limit;
7
     const skip = (page - 1) * limit;
8
-    const userId = req.user.id;
8
+    const userId = req.tokenData.sub;
9
 
9
 
10
     // 1. Ambil provinsi yang menjadi area kerja sales
10
     // 1. Ambil provinsi yang menjadi area kerja sales
11
     const userAreas = await prisma.userArea.findMany({
11
     const userAreas = await prisma.userArea.findMany({

+ 66 - 29
src/utils/LogActivity.js

@@ -1,43 +1,80 @@
1
 const jwt = require('jsonwebtoken');
1
 const jwt = require('jsonwebtoken');
2
 const prisma = require('../prisma/PrismaClient.js');
2
 const prisma = require('../prisma/PrismaClient.js');
3
-const timeLocal = require('../utils/TimeLocal.js')
3
+const timeLocal = require('../utils/TimeLocal.js');
4
+const { getUserNameById } = require('./CheckUserKeycloak.js');
4
 
5
 
5
-const baseLog = async ({ req, action, data }) => {
6
-    // try {
7
-    //     const authHeader = req.headers.authorization;
8
-    //     if (!authHeader || !authHeader.startsWith('Bearer ')) return;
6
+// const baseLog = async ({ req, action, data }) => {
7
+// 1
8
+// try {
9
+//     const authHeader = req.headers.authorization;
10
+//     if (!authHeader || !authHeader.startsWith('Bearer ')) return;
11
+
12
+//     const token = authHeader.split(' ')[1];
13
+//     const decoded = jwt.decode(token);
14
+//     if (!decoded?.id || !decoded?.username) return;
15
+
16
+//     await prisma.activityLog.create({
17
+//         data: {
18
+//             user_id: decoded.id,
19
+//             username: decoded.username,
20
+//             action: JSON.stringify({ [action]: data }),
21
+//             createdAt: timeLocal.now().toDate(),
22
+//             updatedAt: timeLocal.now().toDate(),
23
+//             deletedAt: null,
24
+//         }
25
+//     });
26
+// } catch (err) {
27
+//     console.error('Failed to log activity:', err.message);
28
+// }
29
+
30
+// 2
31
+// try {
32
+//     let userId, username;
9
 
33
 
10
-    //     const token = authHeader.split(' ')[1];
11
-    //     const decoded = jwt.decode(token);
12
-    //     if (!decoded?.id || !decoded?.username) return;
34
+//     // Coba ambil dari Bearer token jika ada
35
+//     const authHeader = req?.headers?.authorization;
36
+//     if (authHeader?.startsWith('Bearer ')) {
37
+//         const token = authHeader.split(' ')[1];
38
+//         const decoded = jwt.decode(token);
39
+//         userId = decoded?.id;
40
+//         username = decoded?.username;
41
+//     }
13
 
42
 
14
-    //     await prisma.activityLog.create({
15
-    //         data: {
16
-    //             user_id: decoded.id,
17
-    //             username: decoded.username,
18
-    //             action: JSON.stringify({ [action]: data }),
19
-    //             createdAt: timeLocal.now().toDate(),
20
-    //             updatedAt: timeLocal.now().toDate(),
21
-    //             deletedAt: null,
22
-    //         }
23
-    //     });
24
-    // } catch (err) {
25
-    //     console.error('Failed to log activity:', err.message);
26
-    // }
43
+//     // Fallback ke data langsung jika belum dapat dari token
44
+//     if (!userId || !username) {
45
+//         userId = data?.id;
46
+//         username = data?.username;
47
+//     }
27
 
48
 
49
+//     if (!userId || !username) return;
50
+
51
+//     await prisma.activityLog.create({
52
+//         data: {
53
+//             user_id: userId,
54
+//             username,
55
+//             action: JSON.stringify({ [action]: data }),
56
+//             createdAt: timeLocal.now().toDate(),
57
+//             updatedAt: timeLocal.now().toDate(),
58
+//             deletedAt: null,
59
+//         }
60
+//     });
61
+// } catch (err) {
62
+//     console.error('Failed to log activity:', err.message);
63
+// }
64
+
65
+// };
66
+// 3
67
+const baseLog = async ({ req, action, data }) => {
28
     try {
68
     try {
29
         let userId, username;
69
         let userId, username;
30
 
70
 
31
-        // Coba ambil dari Bearer token jika ada
32
-        const authHeader = req?.headers?.authorization;
33
-        if (authHeader?.startsWith('Bearer ')) {
34
-            const token = authHeader.split(' ')[1];
35
-            const decoded = jwt.decode(token);
36
-            userId = decoded?.id;
37
-            username = decoded?.username;
71
+        // Ambil userId dari token Keycloak
72
+        if (req?.tokenData?.sub) {
73
+            userId = req.tokenData.sub;
74
+            username = await getUserNameById(userId);
38
         }
75
         }
39
 
76
 
40
-        // Fallback ke data langsung jika belum dapat dari token
77
+        // Fallback kalau tokenData tidak ada
41
         if (!userId || !username) {
78
         if (!userId || !username) {
42
             userId = data?.id;
79
             userId = data?.id;
43
             username = data?.username;
80
             username = data?.username;