alpe-temp 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/.env.example +1 -1
  2. package/backend-project/package-lock.json +131 -0
  3. package/backend-project/package.json +3 -1
  4. package/backend-project/server-err.txt +0 -0
  5. package/backend-project/server-out.txt +6 -0
  6. package/backend-project/src/app.js +33 -55
  7. package/backend-project/src/config/app.config.js +1 -49
  8. package/backend-project/src/config/env.js +2 -10
  9. package/backend-project/src/middleware/auth.middleware.js +3 -26
  10. package/backend-project/src/modules/auth/auth.controller.js +15 -19
  11. package/backend-project/src/modules/auth/auth.routes.js +4 -8
  12. package/backend-project/src/modules/auth/auth.service.js +9 -31
  13. package/backend-project/src/modules/auth/user.model.js +10 -33
  14. package/backend-project/src/modules/department/department.controller.js +0 -4
  15. package/backend-project/src/modules/department/department.model.js +1 -4
  16. package/backend-project/src/modules/department/department.routes.js +0 -1
  17. package/backend-project/src/modules/department/department.service.js +1 -9
  18. package/backend-project/src/modules/employee/employee.controller.js +2 -10
  19. package/backend-project/src/modules/employee/employee.model.js +15 -9
  20. package/backend-project/src/modules/employee/employee.routes.js +4 -6
  21. package/backend-project/src/modules/employee/employee.service.js +20 -5
  22. package/backend-project/src/modules/position/position.controller.js +50 -0
  23. package/backend-project/src/modules/position/position.model.js +8 -0
  24. package/backend-project/src/modules/position/position.routes.js +14 -0
  25. package/backend-project/src/modules/position/position.service.js +21 -0
  26. package/backend-project/src/modules/reports/reports.controller.js +159 -24
  27. package/backend-project/src/modules/reports/reports.routes.js +3 -2
  28. package/backend-project/src/seed.js +69 -15
  29. package/backend-project/src/utils/token.js +1 -27
  30. package/backend-project/test-all-routes.js +294 -0
  31. package/bin/epms.js +57 -92
  32. package/frontend-project/dist/assets/index-CRG9iE0k.css +1 -0
  33. package/frontend-project/dist/assets/{index-B08ICGra.js → index-LpBGz8lQ.js} +7 -7
  34. package/frontend-project/dist/index.html +3 -4
  35. package/frontend-project/index.html +1 -2
  36. package/frontend-project/src/Auth/Login.jsx +15 -25
  37. package/frontend-project/src/Auth/Register.jsx +91 -183
  38. package/frontend-project/src/Intro.jsx +4 -9
  39. package/frontend-project/src/LayOut.jsx +10 -23
  40. package/frontend-project/src/api/ApiClient.js +20 -60
  41. package/frontend-project/src/config.js +4 -4
  42. package/frontend-project/src/layouts/BottomNav.jsx +23 -106
  43. package/frontend-project/src/layouts/TopNav.jsx +19 -99
  44. package/frontend-project/src/layouts/useShell.js +30 -44
  45. package/frontend-project/src/main.jsx +2 -3
  46. package/frontend-project/src/pages/Department.jsx +21 -58
  47. package/frontend-project/src/pages/Employee.jsx +131 -113
  48. package/frontend-project/src/pages/Home.jsx +36 -36
  49. package/frontend-project/src/pages/Position.jsx +161 -0
  50. package/frontend-project/src/pages/Reports.jsx +112 -67
  51. package/package.json +2 -12
  52. package/server-test-err.txt +0 -0
  53. package/server-test-out.txt +0 -0
  54. package/backend-project/src/modules/_example/example.controller.js +0 -82
  55. package/backend-project/src/modules/_example/example.model.js +0 -47
  56. package/backend-project/src/modules/_example/example.routes.js +0 -43
  57. package/backend-project/src/modules/_example/example.service.js +0 -58
  58. package/backend-project/src/modules/excel/excel.controller.js +0 -61
  59. package/backend-project/src/modules/excel/excel.routes.js +0 -13
  60. package/backend-project/src/modules/excel/excel.service.js +0 -303
  61. package/backend-project/src/modules/salary/salary.controller.js +0 -70
  62. package/backend-project/src/modules/salary/salary.model.js +0 -23
  63. package/backend-project/src/modules/salary/salary.routes.js +0 -16
  64. package/backend-project/src/modules/salary/salary.service.js +0 -44
  65. package/frontend-project/dist/assets/index-D_cqT2Z6.css +0 -1
@@ -1,61 +0,0 @@
1
- const ExcelService = require('./excel.service');
2
- const res_ = require('../../utils/response');
3
-
4
- const ExcelController = {
5
- /**
6
- * GET /api/excel/export/users
7
- * Demo: exports a users sheet directly to the browser.
8
- * Replace `sampleData` with a real DB query.
9
- */
10
- async exportUsers(req, res) {
11
- try {
12
- const sampleData = [
13
- { id: 'u-001', name: 'Alice Martin', email: 'alice@example.com', role: 'admin', joined: new Date('2024-01-15'), revenue: 4800 },
14
- { id: 'u-002', name: 'Bob Karenzi', email: 'bob@example.com', role: 'user', joined: new Date('2024-03-22'), revenue: 1200 },
15
- { id: 'u-003', name: 'Claire Umutesi', email: 'claire@example.com', role: 'user', joined: new Date('2024-07-09'), revenue: 2500 },
16
- ];
17
-
18
- const sheets = [
19
- {
20
- name: 'Users',
21
- columns: [
22
- { header: 'ID', key: 'id', width: 10 },
23
- { header: 'Name', key: 'name', width: 25 },
24
- { header: 'Email', key: 'email', width: 35 },
25
- { header: 'Role', key: 'role', width: 12 },
26
- { header: 'Joined', key: 'joined', width: 14, type: 'date' },
27
- { header: 'Revenue', key: 'revenue', width: 15, type: 'currency' },
28
- ],
29
- rows: sampleData,
30
- totals: { revenue: `=SUM(F2:F${sampleData.length + 1})` },
31
- },
32
- ];
33
-
34
- await ExcelService.exportToResponse(res, 'users-report', sheets);
35
- } catch (err) {
36
- return res_.error(res, err.message);
37
- }
38
- },
39
-
40
- /**
41
- * GET /api/excel/export/custom
42
- * Generic export: pass `sheetName` + `data` as JSON body or query params.
43
- * Useful for ad-hoc client-driven exports.
44
- */
45
- async exportCustom(req, res) {
46
- try {
47
- const { sheetName = 'Export', data = [] } = req.body;
48
-
49
- if (!Array.isArray(data) || !data.length) {
50
- return res_.badRequest(res, '`data` must be a non-empty array');
51
- }
52
-
53
- const sheet = ExcelService.buildSheetDef(sheetName, data);
54
- await ExcelService.exportToResponse(res, sheetName, [sheet]);
55
- } catch (err) {
56
- return res_.error(res, err.message);
57
- }
58
- },
59
- };
60
-
61
- module.exports = ExcelController;
@@ -1,13 +0,0 @@
1
- const { Router } = require('express');
2
- const ExcelController = require('./excel.controller');
3
- const { authenticate } = require('../../middleware/auth.middleware');
4
-
5
- const router = Router();
6
-
7
- // All export routes require authentication
8
- router.use(authenticate);
9
-
10
- router.get('/export/users', ExcelController.exportUsers);
11
- router.post('/export/custom', ExcelController.exportCustom);
12
-
13
- module.exports = router;
@@ -1,303 +0,0 @@
1
- /**
2
- * excel.service.js
3
- * ─────────────────────────────────────────────────────────────────────────────
4
- * A fast, reusable Excel export utility built on ExcelJS.
5
- *
6
- * QUICK START
7
- * ───────────
8
- * const ExcelService = require('./excel.service');
9
- *
10
- * // 1. Stream directly to an HTTP response
11
- * await ExcelService.exportToResponse(res, 'users-report', [
12
- * {
13
- * name: 'Users',
14
- * columns: [
15
- * { header: 'ID', key: 'id', width: 36 },
16
- * { header: 'Name', key: 'name', width: 25 },
17
- * { header: 'Email', key: 'email', width: 35 },
18
- * ],
19
- * rows: users, // array of objects
20
- * },
21
- * ]);
22
- *
23
- * // 2. Save to disk and get the file path back
24
- * const filePath = await ExcelService.exportToFile('monthly-report', sheets, {
25
- * outputDir: './exports',
26
- * });
27
- *
28
- * // 3. Get a raw Buffer (e.g. to upload to S3)
29
- * const buffer = await ExcelService.exportToBuffer('report', sheets);
30
- * ─────────────────────────────────────────────────────────────────────────────
31
- */
32
-
33
- const ExcelJS = require('exceljs');
34
- const fs = require('fs');
35
- const path = require('path');
36
- const env = require('../../config/env');
37
-
38
- // ─── Colour palette ──────────────────────────────────────────────────────────
39
- const THEME = {
40
- headerBg: 'FF2563EB', // Blue-600
41
- headerFg: 'FFFFFFFF', // White
42
- altRowBg: 'FFF0F4FF', // Light blue-50
43
- borderColor:'FFB0C4DE', // Muted blue-200
44
- totalBg: 'FFEFF6FF', // Blue-50
45
- totalFg: 'FF1D4ED8', // Blue-700
46
- };
47
-
48
- // ─── Shared styles ────────────────────────────────────────────────────────────
49
- const HEADER_STYLE = {
50
- font: { name: 'Arial', bold: true, size: 11, color: { argb: THEME.headerFg } },
51
- fill: { type: 'pattern', pattern: 'solid', fgColor: { argb: THEME.headerBg } },
52
- alignment: { horizontal: 'center', vertical: 'middle', wrapText: false },
53
- border: {
54
- top: { style: 'thin', color: { argb: THEME.borderColor } },
55
- bottom: { style: 'medium', color: { argb: THEME.headerBg } },
56
- },
57
- };
58
-
59
- const CELL_BORDER = {
60
- bottom: { style: 'hair', color: { argb: THEME.borderColor } },
61
- right: { style: 'hair', color: { argb: THEME.borderColor } },
62
- };
63
-
64
- // ─── Helpers ─────────────────────────────────────────────────────────────────
65
-
66
- /** Apply header row styling */
67
- const styleHeaderRow = (row) => {
68
- row.height = 28;
69
- row.eachCell((cell) => {
70
- Object.assign(cell, HEADER_STYLE);
71
- cell.font = { ...HEADER_STYLE.font };
72
- cell.fill = { ...HEADER_STYLE.fill, fgColor: { argb: THEME.headerBg } };
73
- cell.alignment = { ...HEADER_STYLE.alignment };
74
- cell.border = { ...HEADER_STYLE.border };
75
- });
76
- };
77
-
78
- /** Apply alternating row colours + light borders */
79
- const styleDataRow = (row, rowIndex) => {
80
- row.height = 20;
81
- const isAlt = rowIndex % 2 === 0;
82
- row.eachCell({ includeEmpty: true }, (cell) => {
83
- cell.font = { name: 'Arial', size: 10 };
84
- cell.alignment = { vertical: 'middle' };
85
- cell.border = CELL_BORDER;
86
- if (isAlt) {
87
- cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: THEME.altRowBg } };
88
- }
89
- });
90
- };
91
-
92
- /** Format a column's cells according to a `type` hint */
93
- const applyColumnFormat = (cell, value, type) => {
94
- switch (type) {
95
- case 'currency':
96
- cell.numFmt = '"$"#,##0.00;[Red]("$"#,##0.00)';
97
- cell.value = typeof value === 'string' ? parseFloat(value) || 0 : value;
98
- break;
99
- case 'percent':
100
- cell.numFmt = '0.0%';
101
- cell.value = typeof value === 'string' ? parseFloat(value) || 0 : value;
102
- break;
103
- case 'date':
104
- cell.numFmt = 'yyyy-mm-dd';
105
- cell.value = value instanceof Date ? value : new Date(value);
106
- break;
107
- case 'number':
108
- cell.numFmt = '#,##0.##';
109
- cell.value = typeof value === 'string' ? parseFloat(value) || 0 : value;
110
- break;
111
- default:
112
- cell.value = value ?? '';
113
- }
114
- };
115
-
116
- /**
117
- * buildWorkbook
118
- * Populates a workbook from a `sheets` array definition.
119
- *
120
- * @param {ExcelJS.Workbook} wb
121
- * @param {SheetDef[]} sheets
122
- *
123
- * SheetDef {
124
- * name: string – tab name
125
- * columns: ColumnDef[] – column definitions
126
- * rows: object[] – data rows
127
- * totals?: Record<string, string> – key → Excel formula string, e.g. { amount: '=SUM(C2:C100)' }
128
- * freezeHeader?: boolean – default true
129
- * autoFilter?: boolean – default true
130
- * extraSheets?: SheetDef[] – nested; same API
131
- * }
132
- *
133
- * ColumnDef {
134
- * header: string
135
- * key: string
136
- * width?: number (default 18)
137
- * type?: 'text' | 'number' | 'currency' | 'percent' | 'date'
138
- * hidden?: boolean
139
- * }
140
- */
141
- const buildWorkbook = (wb, sheets) => {
142
- for (const sheetDef of sheets) {
143
- const {
144
- name,
145
- columns,
146
- rows = [],
147
- totals,
148
- freezeHeader = true,
149
- autoFilter = true,
150
- } = sheetDef;
151
-
152
- const ws = wb.addWorksheet(name, {
153
- pageSetup: { fitToPage: true, fitToWidth: 1, orientation: 'landscape' },
154
- });
155
-
156
- // ── Columns ──────────────────────────────────────────────────────────────
157
- ws.columns = columns.map(({ header, key, width = 18, hidden = false }) => ({
158
- header,
159
- key,
160
- width,
161
- hidden,
162
- }));
163
-
164
- // ── Header row ───────────────────────────────────────────────────────────
165
- styleHeaderRow(ws.getRow(1));
166
-
167
- // ── Data rows ────────────────────────────────────────────────────────────
168
- rows.forEach((rowData, idx) => {
169
- const row = ws.addRow({});
170
- columns.forEach(({ key, type = 'text' }, colIdx) => {
171
- const cell = row.getCell(colIdx + 1);
172
- applyColumnFormat(cell, rowData[key], type);
173
- });
174
- styleDataRow(row, idx + 1);
175
- });
176
-
177
- // ── Totals row ───────────────────────────────────────────────────────────
178
- if (totals && Object.keys(totals).length) {
179
- const totalRow = ws.addRow({});
180
- totalRow.height = 22;
181
- columns.forEach(({ key }, colIdx) => {
182
- const cell = totalRow.getCell(colIdx + 1);
183
- if (totals[key]) {
184
- cell.value = { formula: totals[key] };
185
- cell.font = { name: 'Arial', bold: true, size: 10, color: { argb: THEME.totalFg } };
186
- cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: THEME.totalBg } };
187
- } else if (colIdx === 0) {
188
- cell.value = 'TOTAL';
189
- cell.font = { name: 'Arial', bold: true, size: 10 };
190
- cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: THEME.totalBg } };
191
- }
192
- cell.border = CELL_BORDER;
193
- cell.alignment = { vertical: 'middle' };
194
- });
195
- }
196
-
197
- // ── Auto-filter on header ─────────────────────────────────────────────────
198
- if (autoFilter && columns.length) {
199
- ws.autoFilter = {
200
- from: { row: 1, column: 1 },
201
- to: { row: 1, column: columns.length },
202
- };
203
- }
204
-
205
- // ── Freeze header row ────────────────────────────────────────────────────
206
- if (freezeHeader) {
207
- ws.views = [{ state: 'frozen', ySplit: 1 }];
208
- }
209
- }
210
- };
211
-
212
- // ─── Public API ──────────────────────────────────────────────────────────────
213
-
214
- const ExcelService = {
215
- /**
216
- * exportToResponse
217
- * Stream the workbook directly to an Express `res` object.
218
- *
219
- * @param {import('express').Response} res
220
- * @param {string} filename – without extension
221
- * @param {SheetDef[]} sheets
222
- */
223
- async exportToResponse(res, filename, sheets) {
224
- const wb = new ExcelJS.Workbook();
225
- wb.creator = 'App Export';
226
- wb.created = new Date();
227
- buildWorkbook(wb, sheets);
228
-
229
- const safe = filename.replace(/[^a-z0-9_-]/gi, '_');
230
- res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
231
- res.setHeader('Content-Disposition', `attachment; filename="${safe}.xlsx"`);
232
- res.setHeader('Cache-Control', 'no-cache');
233
-
234
- await wb.xlsx.write(res);
235
- res.end();
236
- },
237
-
238
- /**
239
- * exportToBuffer
240
- * Returns the workbook as a Node.js Buffer (for uploads, email attachments, etc.)
241
- *
242
- * @param {string} filename – metadata only
243
- * @param {SheetDef[]} sheets
244
- * @returns {Promise<Buffer>}
245
- */
246
- async exportToBuffer(filename, sheets) {
247
- const wb = new ExcelJS.Workbook();
248
- wb.creator = filename;
249
- wb.created = new Date();
250
- buildWorkbook(wb, sheets);
251
- return wb.xlsx.writeBuffer();
252
- },
253
-
254
- /**
255
- * exportToFile
256
- * Saves the workbook to disk and returns the absolute file path.
257
- *
258
- * @param {string} filename
259
- * @param {SheetDef[]} sheets
260
- * @param {{ outputDir?: string }} options
261
- * @returns {Promise<string>} absolute path to the saved file
262
- */
263
- async exportToFile(filename, sheets, options = {}) {
264
- const outputDir = options.outputDir || env.EXCEL_OUTPUT_DIR;
265
- fs.mkdirSync(outputDir, { recursive: true });
266
-
267
- const safe = filename.replace(/[^a-z0-9_-]/gi, '_');
268
- const stamp = new Date().toISOString().replace(/[:.]/g, '-');
269
- const filePath = path.resolve(outputDir, `${safe}_${stamp}.xlsx`);
270
-
271
- const wb = new ExcelJS.Workbook();
272
- wb.creator = filename;
273
- wb.created = new Date();
274
- buildWorkbook(wb, sheets);
275
- await wb.xlsx.writeFile(filePath);
276
-
277
- return filePath;
278
- },
279
-
280
- /**
281
- * buildSheetDef
282
- * Convenience factory — infers column keys from the first row of data.
283
- *
284
- * @param {string} name – sheet tab name
285
- * @param {object[]} rows – data rows
286
- * @param {object} opts – { columnWidths, columnTypes, totals, ... }
287
- */
288
- buildSheetDef(name, rows, opts = {}) {
289
- const { columnWidths = {}, columnTypes = {}, totals, freezeHeader, autoFilter } = opts;
290
- const keys = rows.length ? Object.keys(rows[0]) : [];
291
-
292
- const columns = keys.map((key) => ({
293
- header: key.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()),
294
- key,
295
- width: columnWidths[key] || 18,
296
- type: columnTypes[key] || 'text',
297
- }));
298
-
299
- return { name, columns, rows, totals, freezeHeader, autoFilter };
300
- },
301
- };
302
-
303
- module.exports = ExcelService;
@@ -1,70 +0,0 @@
1
- const SalaryService = require('./salary.service');
2
- const res_ = require('../../utils/response');
3
-
4
- const SalaryController = {
5
- async create(req, res) {
6
- try {
7
- const data = req.body;
8
- data.netSalary = data.grossSalary - data.totalDeduction;
9
- const sal = await SalaryService.create(data);
10
- return res_.created(res, sal, 'Salary record created');
11
- } catch (err) {
12
- console.error(`[SalaryController.create]`, err);
13
- return res_.error(res, err.message, err.statusCode || 500);
14
- }
15
- },
16
-
17
- async list(req, res) {
18
- try {
19
- const salaries = await SalaryService.list();
20
- return res_.success(res, { salaries });
21
- } catch (err) {
22
- return res_.error(res, err.message, 500);
23
- }
24
- },
25
-
26
- async getById(req, res) {
27
- try {
28
- const sal = await SalaryService.getById(req.params.id);
29
- if (!sal) return res_.notFound(res, 'Salary record not found');
30
- return res_.success(res, sal);
31
- } catch (err) {
32
- return res_.error(res, err.message, 500);
33
- }
34
- },
35
-
36
- async update(req, res) {
37
- try {
38
- const data = req.body;
39
- if (data.grossSalary !== undefined && data.totalDeduction !== undefined) {
40
- data.netSalary = data.grossSalary - data.totalDeduction;
41
- }
42
- const sal = await SalaryService.update(req.params.id, data);
43
- if (!sal) return res_.notFound(res, 'Salary record not found');
44
- return res_.success(res, sal, 'Salary record updated');
45
- } catch (err) {
46
- return res_.error(res, err.message, 500);
47
- }
48
- },
49
-
50
- async remove(req, res) {
51
- try {
52
- const sal = await SalaryService.remove(req.params.id);
53
- if (!sal) return res_.notFound(res, 'Salary record not found');
54
- return res_.success(res, null, 'Salary record deleted');
55
- } catch (err) {
56
- return res_.error(res, err.message, 500);
57
- }
58
- },
59
-
60
- async average(req, res) {
61
- try {
62
- const avg = await SalaryService.average();
63
- return res_.success(res, { averageSalary: avg });
64
- } catch (err) {
65
- return res_.error(res, err.message, 500);
66
- }
67
- },
68
- };
69
-
70
- module.exports = SalaryController;
@@ -1,23 +0,0 @@
1
- const mongoose = require('mongoose');
2
-
3
- const salarySchema = new mongoose.Schema({
4
- employee: { type: mongoose.Schema.Types.ObjectId, ref: 'Employee', required: true },
5
- grossSalary: { type: Number, required: true },
6
- totalDeduction: { type: Number, required: true, default: 0 },
7
- netSalary: { type: Number, required: true },
8
- month: { type: String, required: true, trim: true },
9
- }, { timestamps: true });
10
-
11
- const Salary = mongoose.model('Salary', salarySchema);
12
-
13
- // Drop stale indexes from previous schema versions
14
- mongoose.connection.once('open', async () => {
15
- try {
16
- await Salary.collection.dropIndex('employeeNumber_1');
17
- } catch {}
18
- try {
19
- await Salary.collection.dropIndex('salaryId_1');
20
- } catch {}
21
- });
22
-
23
- module.exports = Salary;
@@ -1,16 +0,0 @@
1
- const { Router } = require('express');
2
- const SalaryController = require('./salary.controller');
3
- const { authenticate } = require('../../middleware/auth.middleware');
4
-
5
- const router = Router();
6
-
7
- router.use(authenticate);
8
-
9
- router.post('/', SalaryController.create);
10
- router.get('/', SalaryController.list);
11
- router.get('/avg', SalaryController.average);
12
- router.get('/:id', SalaryController.getById);
13
- router.put('/:id', SalaryController.update);
14
- router.delete('/:id', SalaryController.remove);
15
-
16
- module.exports = router;
@@ -1,44 +0,0 @@
1
- const Salary = require('./salary.model');
2
-
3
- const SalaryService = {
4
- async create(data) {
5
- const sal = await Salary.create(data);
6
- return Salary.populate(sal, { path: 'employee', populate: { path: 'department' } });
7
- },
8
-
9
- async list() {
10
- return Salary.find()
11
- .populate({ path: 'employee', populate: { path: 'department' } })
12
- .sort({ createdAt: -1 });
13
- },
14
-
15
- async getById(id) {
16
- return Salary.findById(id)
17
- .populate({ path: 'employee', populate: { path: 'department' } });
18
- },
19
-
20
- async update(id, data) {
21
- return Salary.findByIdAndUpdate(id, data, { new: true })
22
- .populate({ path: 'employee', populate: { path: 'department' } });
23
- },
24
-
25
- async remove(id) {
26
- return Salary.findByIdAndDelete(id);
27
- },
28
-
29
- async average() {
30
- const result = await Salary.aggregate([
31
- { $group: { _id: null, avgSalary: { $avg: '$netSalary' } } },
32
- ]);
33
- return result.length > 0 ? result[0].avgSalary : 0;
34
- },
35
-
36
- async getMonthlyReport(month) {
37
- const match = month ? { month } : {};
38
- return Salary.find(match)
39
- .populate({ path: 'employee', populate: { path: 'department' } })
40
- .sort({ month: -1, createdAt: -1 });
41
- },
42
- };
43
-
44
- module.exports = SalaryService;
@@ -1 +0,0 @@
1
- *,:before,:after,::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border:0 solid #e5e7eb}:before,:after{--tw-content:""}html,:host{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{line-height:inherit;margin:0}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-feature-settings:normal;font-variation-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-feature-settings:inherit;font-variation-settings:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:#0000;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{margin:0;padding:0;list-style:none}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder{opacity:1;color:#9ca3af}textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (width>=640px){.container{max-width:640px}}@media (width>=768px){.container{max-width:768px}}@media (width>=1024px){.container{max-width:1024px}}@media (width>=1280px){.container{max-width:1280px}}@media (width>=1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.right-0{right:0}.right-3{right:.75rem}.right-5{right:1.25rem}.top-0{top:0}.top-1\/2{top:50%}.top-5{top:1.25rem}.top-full{top:100%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.z-\[9999\]{z-index:9999}.col-span-2{grid-column:span 2/span 2}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-auto{margin-bottom:auto}.ml-0\.5{margin-left:.125rem}.ml-auto{margin-left:auto}.mr-4{margin-right:1rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-10{margin-top:2.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-auto{margin-top:auto}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-20{height:5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[500px\]{height:500px}.h-\[52px\]{height:52px}.h-\[540px\]{height:540px}.h-\[64px\]{height:64px}.h-screen{height:100vh}.min-h-screen{min-height:100vh}.w-16{width:4rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-5\/12{width:41.6667%}.w-6{width:1.5rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-fit{width:fit-content}.w-full{width:100%}.min-w-\[150px\]{min-width:150px}.min-w-\[240px\]{min-width:240px}.min-w-max{min-width:max-content}.max-w-2xl{max-width:42rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-\[760px\]{max-width:760px}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xl{max-width:36rem}.max-w-xs{max-width:20rem}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y:-50%;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate:180deg;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:2s cubic-bezier(.4,0,.6,1) infinite pulse}@keyframes spin{to{transform:rotate(360deg)}}.select-none{-webkit-user-select:none;user-select:none}.resize-none{resize:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.25rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-none{border-radius:0}.rounded-sm{border-radius:.125rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-blue-200{--tw-border-opacity:1;border-color:rgb(191 219 254/var(--tw-border-opacity,1))}.border-emerald-200{--tw-border-opacity:1;border-color:rgb(167 243 208/var(--tw-border-opacity,1))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-red-200{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity,1))}.border-white\/20{border-color:#fff3}.border-zinc-200{--tw-border-opacity:1;border-color:rgb(228 228 231/var(--tw-border-opacity,1))}.border-t-\[\#008A75\]{--tw-border-opacity:1;border-top-color:rgb(0 138 117/var(--tw-border-opacity,1))}.bg-\[\#008A75\]{--tw-bg-opacity:1;background-color:rgb(0 138 117/var(--tw-bg-opacity,1))}.bg-\[\#E6F7F5\]{--tw-bg-opacity:1;background-color:rgb(230 247 245/var(--tw-bg-opacity,1))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.bg-black\/40{background-color:#0006}.bg-emerald-50{--tw-bg-opacity:1;background-color:rgb(236 253 245/var(--tw-bg-opacity,1))}.bg-emerald-500{--tw-bg-opacity:1;background-color:rgb(16 185 129/var(--tw-bg-opacity,1))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-zinc-50{--tw-bg-opacity:1;background-color:rgb(250 250 250/var(--tw-bg-opacity,1))}.p-10{padding:2.5rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-10{padding-top:2.5rem;padding-bottom:2.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pr-10{padding-right:2.5rem}.pr-4{padding-right:1rem}.pt-10{padding-top:2.5rem}.pt-3{padding-top:.75rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.text-\[18px\]{font-size:18px}.text-\[20px\]{font-size:20px}.text-\[22px\]{font-size:22px}.text-\[32px\]{font-size:32px}.text-\[7px\]{font-size:7px}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-snug{line-height:1.375}.leading-tight{line-height:1.25}.tracking-\[0\.2em\]{letter-spacing:.2em}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-\[\#008A75\]{--tw-text-opacity:1;color:rgb(0 138 117/var(--tw-text-opacity,1))}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.text-emerald-500{--tw-text-opacity:1;color:rgb(16 185 129/var(--tw-text-opacity,1))}.text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity,1))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-white\/40{color:#fff6}.text-white\/50{color:#ffffff80}.text-white\/60{color:#fff9}.text-zinc-800{--tw-text-opacity:1;color:rgb(39 39 42/var(--tw-text-opacity,1))}.text-zinc-800\/60{color:#27272a99}.text-zinc-900{--tw-text-opacity:1;color:rgb(24 24 27/var(--tw-text-opacity,1))}.opacity-60{opacity:.6}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000), var(--tw-ring-shadow,0 0 #0000), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px #0000001a, 0 8px 10px -6px #0000001a;--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000), var(--tw-ring-shadow,0 0 #0000), var(--tw-shadow)}.outline{outline-style:solid}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-\[2px\]{--tw-backdrop-blur:blur(2px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-all{transition-property:all;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-property:transform;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-100{transition-duration:.1s}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}:root{--color-primary:#008a75;--color-surface:#f9fafb;--color-card:#fff;--color-border:#e5e7eb;--color-text:#1f2937;--color-text-muted:#9ca3af;--color-nav-bg:#fff;--color-nav-text:#4b5563;--color-nav-active:#008a75;--color-danger:#ef4444;--color-success:#10b981;--color-warning:#f59e0b;--color-info:#3b82f6;--radius:.375rem;--text-xs:.75rem;--text-sm:.8125rem;--text-base:.875rem;--text-lg:1rem;--text-xl:1.25rem;--text-2xl:1.5rem;--text-3xl:2rem}.animate-spin{animation:1s linear infinite spin}.placeholder\:text-gray-400::placeholder{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.hover\:bg-emerald-600:hover{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity,1))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.hover\:bg-gray-900:hover{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.hover\:text-gray-500:hover{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-70:hover{opacity:.7}.hover\:opacity-80:hover{opacity:.8}.hover\:opacity-90:hover{opacity:.9}.focus\:outline-none:focus{outline-offset:2px;outline:2px solid #0000}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow,0 0 #0000)}.focus\:ring-\[var\(--color-primary\)\]:focus{--tw-ring-color:var(--color-primary)}.focus\:ring-emerald-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(52 211 153/var(--tw-ring-opacity,1))}.focus\:ring-gray-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(229 231 235/var(--tw-ring-opacity,1))}.focus\:ring-gray-300:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(209 213 219/var(--tw-ring-opacity,1))}.focus\:ring-gray-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(75 85 99/var(--tw-ring-opacity,1))}.focus\:ring-red-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(248 113 113/var(--tw-ring-opacity,1))}.focus\:ring-offset-1:focus{--tw-ring-offset-width:1px}.active\:scale-\[0\.98\]:active{--tw-scale-x:.98;--tw-scale-y:.98;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-gray-50:disabled{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.disabled\:text-gray-400:disabled{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.disabled\:opacity-50:disabled{opacity:.5}@media (width>=640px){.sm\:inline{display:inline}.sm\:flex{display:flex}.sm\:p-10{padding:2.5rem}}@media (width>=1024px){.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}