alpe-temp 1.0.2 → 1.0.3

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 (60) hide show
  1. package/backend-project/package-lock.json +131 -0
  2. package/backend-project/package.json +3 -1
  3. package/backend-project/src/app.js +33 -55
  4. package/backend-project/src/config/app.config.js +1 -49
  5. package/backend-project/src/config/env.js +2 -10
  6. package/backend-project/src/middleware/auth.middleware.js +3 -26
  7. package/backend-project/src/modules/auth/auth.controller.js +15 -19
  8. package/backend-project/src/modules/auth/auth.routes.js +4 -8
  9. package/backend-project/src/modules/auth/auth.service.js +9 -31
  10. package/backend-project/src/modules/auth/user.model.js +10 -33
  11. package/backend-project/src/modules/department/department.controller.js +0 -4
  12. package/backend-project/src/modules/department/department.model.js +1 -4
  13. package/backend-project/src/modules/department/department.routes.js +0 -1
  14. package/backend-project/src/modules/department/department.service.js +1 -9
  15. package/backend-project/src/modules/employee/employee.controller.js +2 -10
  16. package/backend-project/src/modules/employee/employee.model.js +15 -9
  17. package/backend-project/src/modules/employee/employee.routes.js +4 -6
  18. package/backend-project/src/modules/employee/employee.service.js +20 -5
  19. package/backend-project/src/modules/position/position.controller.js +50 -0
  20. package/backend-project/src/modules/position/position.model.js +8 -0
  21. package/backend-project/src/modules/position/position.routes.js +14 -0
  22. package/backend-project/src/modules/position/position.service.js +21 -0
  23. package/backend-project/src/modules/reports/reports.controller.js +16 -28
  24. package/backend-project/src/modules/reports/reports.routes.js +2 -2
  25. package/backend-project/src/seed.js +69 -15
  26. package/backend-project/src/utils/token.js +1 -27
  27. package/frontend-project/dist/assets/index-BXwcQ8Za.css +1 -0
  28. package/frontend-project/dist/assets/index-Bo0aORq7.js +20 -0
  29. package/frontend-project/dist/index.html +3 -3
  30. package/frontend-project/index.html +1 -1
  31. package/frontend-project/src/Auth/Login.jsx +15 -25
  32. package/frontend-project/src/Auth/Register.jsx +92 -183
  33. package/frontend-project/src/Intro.jsx +4 -9
  34. package/frontend-project/src/LayOut.jsx +10 -23
  35. package/frontend-project/src/api/ApiClient.js +19 -60
  36. package/frontend-project/src/layouts/BottomNav.jsx +22 -105
  37. package/frontend-project/src/layouts/TopNav.jsx +19 -98
  38. package/frontend-project/src/layouts/useShell.js +30 -44
  39. package/frontend-project/src/main.jsx +2 -3
  40. package/frontend-project/src/pages/Department.jsx +21 -58
  41. package/frontend-project/src/pages/Employee.jsx +131 -113
  42. package/frontend-project/src/pages/Home.jsx +36 -36
  43. package/frontend-project/src/pages/Position.jsx +161 -0
  44. package/frontend-project/src/pages/Reports.jsx +81 -68
  45. package/package.json +4 -2
  46. package/server-test-err.txt +0 -0
  47. package/server-test-out.txt +0 -0
  48. package/backend-project/src/modules/_example/example.controller.js +0 -82
  49. package/backend-project/src/modules/_example/example.model.js +0 -47
  50. package/backend-project/src/modules/_example/example.routes.js +0 -43
  51. package/backend-project/src/modules/_example/example.service.js +0 -58
  52. package/backend-project/src/modules/excel/excel.controller.js +0 -61
  53. package/backend-project/src/modules/excel/excel.routes.js +0 -13
  54. package/backend-project/src/modules/excel/excel.service.js +0 -303
  55. package/backend-project/src/modules/salary/salary.controller.js +0 -70
  56. package/backend-project/src/modules/salary/salary.model.js +0 -23
  57. package/backend-project/src/modules/salary/salary.routes.js +0 -16
  58. package/backend-project/src/modules/salary/salary.service.js +0 -44
  59. package/frontend-project/dist/assets/index-B08ICGra.js +0 -20
  60. package/frontend-project/dist/assets/index-D_cqT2Z6.css +0 -1
@@ -1,41 +1,18 @@
1
- const { Schema, model } = require('mongoose');
1
+ const mongoose = require('mongoose');
2
2
 
3
- const userSchema = new Schema(
4
- {
5
- name: {
6
- type: String,
7
- required: true,
8
- trim: true,
9
- },
10
- email: {
11
- type: String,
12
- required: true,
13
- unique: true,
14
- lowercase: true,
15
- trim: true,
16
- },
17
- password: {
18
- type: String,
19
- required: true,
20
- },
21
- role: {
22
- type: String,
23
- enum: ['user', 'admin'],
24
- default: 'user',
25
- },
26
- },
27
- { timestamps: true }
28
- );
3
+ const userSchema = new mongoose.Schema({
4
+ userName: { type: String, required: true, unique: true, trim: true },
5
+ password: { type: String, required: true },
6
+ employee: { type: mongoose.Schema.Types.ObjectId, ref: 'Employee' },
7
+ }, { timestamps: true });
29
8
 
30
- // Never expose the password hash in query results
31
9
  userSchema.methods.toSafeObject = function () {
32
10
  return {
33
- id: this._id,
34
- name: this.name,
35
- email: this.email,
36
- role: this.role,
11
+ id: this._id,
12
+ userName: this.userName,
13
+ employee: this.employee,
37
14
  createdAt: this.createdAt,
38
15
  };
39
16
  };
40
17
 
41
- module.exports = model('User', userSchema);
18
+ module.exports = mongoose.model('User', userSchema);
@@ -10,7 +10,6 @@ const DepartmentController = {
10
10
  return res_.error(res, err.message, err.statusCode || 500);
11
11
  }
12
12
  },
13
-
14
13
  async list(req, res) {
15
14
  try {
16
15
  const departments = await DepartmentService.list();
@@ -19,7 +18,6 @@ const DepartmentController = {
19
18
  return res_.error(res, err.message, 500);
20
19
  }
21
20
  },
22
-
23
21
  async getById(req, res) {
24
22
  try {
25
23
  const dept = await DepartmentService.getById(req.params.id);
@@ -29,7 +27,6 @@ const DepartmentController = {
29
27
  return res_.error(res, err.message, 500);
30
28
  }
31
29
  },
32
-
33
30
  async update(req, res) {
34
31
  try {
35
32
  const dept = await DepartmentService.update(req.params.id, req.body);
@@ -39,7 +36,6 @@ const DepartmentController = {
39
36
  return res_.error(res, err.message, 500);
40
37
  }
41
38
  },
42
-
43
39
  async remove(req, res) {
44
40
  try {
45
41
  const dept = await DepartmentService.remove(req.params.id);
@@ -1,10 +1,7 @@
1
1
  const mongoose = require('mongoose');
2
2
 
3
3
  const departmentSchema = new mongoose.Schema({
4
- departmentCode: { type: String, required: true, unique: true, trim: true },
5
- departmentName: { type: String, required: true, trim: true },
6
- grossSalary: { type: Number, required: true },
7
- totalDeduction: { type: Number, required: true, default: 0 },
4
+ departName: { type: String, required: true, unique: true, trim: true },
8
5
  }, { timestamps: true });
9
6
 
10
7
  module.exports = mongoose.model('Department', departmentSchema);
@@ -3,7 +3,6 @@ const DepartmentController = require('./department.controller');
3
3
  const { authenticate } = require('../../middleware/auth.middleware');
4
4
 
5
5
  const router = Router();
6
-
7
6
  router.use(authenticate);
8
7
 
9
8
  router.post('/', DepartmentController.create);
@@ -4,23 +4,15 @@ const DepartmentService = {
4
4
  async create(data) {
5
5
  return Department.create(data);
6
6
  },
7
-
8
7
  async list() {
9
- return Department.find().sort({ departmentCode: 1 });
10
- },
11
-
12
- async getByCode(code) {
13
- return Department.findOne({ departmentCode: code });
8
+ return Department.find().sort({ departName: 1 });
14
9
  },
15
-
16
10
  async getById(id) {
17
11
  return Department.findById(id);
18
12
  },
19
-
20
13
  async update(id, data) {
21
14
  return Department.findByIdAndUpdate(id, data, { new: true });
22
15
  },
23
-
24
16
  async remove(id) {
25
17
  return Department.findByIdAndDelete(id);
26
18
  },
@@ -13,7 +13,8 @@ const EmployeeController = {
13
13
 
14
14
  async list(req, res) {
15
15
  try {
16
- const employees = await EmployeeService.list();
16
+ const { search } = req.query;
17
+ const employees = await EmployeeService.list(search);
17
18
  return res_.success(res, { employees });
18
19
  } catch (err) {
19
20
  return res_.error(res, err.message, 500);
@@ -49,15 +50,6 @@ const EmployeeController = {
49
50
  return res_.error(res, err.message, 500);
50
51
  }
51
52
  },
52
-
53
- async count(req, res) {
54
- try {
55
- const count = await EmployeeService.count();
56
- return res_.success(res, { count });
57
- } catch (err) {
58
- return res_.error(res, err.message, 500);
59
- }
60
- },
61
53
  };
62
54
 
63
55
  module.exports = EmployeeController;
@@ -1,15 +1,21 @@
1
1
  const mongoose = require('mongoose');
2
2
 
3
3
  const employeeSchema = new mongoose.Schema({
4
- employeeNumber: { type: String, required: true, unique: true, trim: true },
5
- firstName: { type: String, required: true, trim: true },
6
- lastName: { type: String, required: true, trim: true },
7
- position: { type: String, required: true, trim: true },
8
- address: { type: String, trim: true },
9
- telephone: { type: String, trim: true },
10
- gender: { type: String, enum: ['male', 'female'], required: true },
11
- hiredDate: { type: Date, required: true },
12
- department: { type: mongoose.Schema.Types.ObjectId, ref: 'Department' },
4
+ empFirstName: { type: String, required: true, trim: true },
5
+ empLastName: { type: String, required: true, trim: true },
6
+ empGender: { type: String, enum: ['male', 'female'], required: true },
7
+ empDateOfBirth: { type: Date },
8
+ empEmail: { type: String, trim: true, lowercase: true },
9
+ empTelephone: { type: String, trim: true },
10
+ empAddress: { type: String, trim: true },
11
+ empHireDate: { type: Date, required: true },
12
+ empStatus: {
13
+ type: String,
14
+ enum: ['on leave', 'left', 'blacklisted', 'deceased', 'on mission'],
15
+ default: 'on mission',
16
+ },
17
+ department: { type: mongoose.Schema.Types.ObjectId, ref: 'Department' },
18
+ position: { type: mongoose.Schema.Types.ObjectId, ref: 'Position' },
13
19
  }, { timestamps: true });
14
20
 
15
21
  module.exports = mongoose.model('Employee', employeeSchema);
@@ -3,14 +3,12 @@ const EmployeeController = require('./employee.controller');
3
3
  const { authenticate } = require('../../middleware/auth.middleware');
4
4
 
5
5
  const router = Router();
6
-
7
6
  router.use(authenticate);
8
7
 
9
- router.post('/', EmployeeController.create);
10
- router.get('/', EmployeeController.list);
11
- router.get('/count', EmployeeController.count);
12
- router.get('/:id', EmployeeController.getById);
13
- router.put('/:id', EmployeeController.update);
8
+ router.post('/', EmployeeController.create);
9
+ router.get('/', EmployeeController.list);
10
+ router.get('/:id', EmployeeController.getById);
11
+ router.put('/:id', EmployeeController.update);
14
12
  router.delete('/:id', EmployeeController.remove);
15
13
 
16
14
  module.exports = router;
@@ -3,19 +3,34 @@ const Employee = require('./employee.model');
3
3
  const EmployeeService = {
4
4
  async create(data) {
5
5
  const emp = await Employee.create(data);
6
- return Employee.populate(emp, { path: 'department' });
6
+ return Employee.populate(emp, [{ path: 'department' }, { path: 'position' }]);
7
7
  },
8
8
 
9
- async list() {
10
- return Employee.find().populate('department').sort({ createdAt: -1 });
9
+ async list(search) {
10
+ const filter = {};
11
+ if (search) {
12
+ const regex = new RegExp(search, 'i');
13
+ filter.$or = [
14
+ { empFirstName: regex },
15
+ { empLastName: regex },
16
+ { empEmail: regex },
17
+ { empTelephone: regex },
18
+ ];
19
+ }
20
+ return Employee.find(filter)
21
+ .populate('department')
22
+ .populate('position')
23
+ .sort({ createdAt: -1 });
11
24
  },
12
25
 
13
26
  async getById(id) {
14
- return Employee.findById(id).populate('department');
27
+ return Employee.findById(id).populate('department').populate('position');
15
28
  },
16
29
 
17
30
  async update(id, data) {
18
- return Employee.findByIdAndUpdate(id, data, { new: true }).populate('department');
31
+ return Employee.findByIdAndUpdate(id, data, { new: true })
32
+ .populate('department')
33
+ .populate('position');
19
34
  },
20
35
 
21
36
  async remove(id) {
@@ -0,0 +1,50 @@
1
+ const PositionService = require('./position.service');
2
+ const res_ = require('../../utils/response');
3
+
4
+ const PositionController = {
5
+ async create(req, res) {
6
+ try {
7
+ const pos = await PositionService.create(req.body);
8
+ return res_.created(res, pos, 'Position created');
9
+ } catch (err) {
10
+ return res_.error(res, err.message, err.statusCode || 500);
11
+ }
12
+ },
13
+ async list(req, res) {
14
+ try {
15
+ const positions = await PositionService.list();
16
+ return res_.success(res, { positions });
17
+ } catch (err) {
18
+ return res_.error(res, err.message, 500);
19
+ }
20
+ },
21
+ async getById(req, res) {
22
+ try {
23
+ const pos = await PositionService.getById(req.params.id);
24
+ if (!pos) return res_.notFound(res, 'Position not found');
25
+ return res_.success(res, pos);
26
+ } catch (err) {
27
+ return res_.error(res, err.message, 500);
28
+ }
29
+ },
30
+ async update(req, res) {
31
+ try {
32
+ const pos = await PositionService.update(req.params.id, req.body);
33
+ if (!pos) return res_.notFound(res, 'Position not found');
34
+ return res_.success(res, pos, 'Position updated');
35
+ } catch (err) {
36
+ return res_.error(res, err.message, 500);
37
+ }
38
+ },
39
+ async remove(req, res) {
40
+ try {
41
+ const pos = await PositionService.remove(req.params.id);
42
+ if (!pos) return res_.notFound(res, 'Position not found');
43
+ return res_.success(res, null, 'Position deleted');
44
+ } catch (err) {
45
+ return res_.error(res, err.message, 500);
46
+ }
47
+ },
48
+ };
49
+
50
+ module.exports = PositionController;
@@ -0,0 +1,8 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const positionSchema = new mongoose.Schema({
4
+ posName: { type: String, required: true, unique: true, trim: true },
5
+ requiredQualification: { type: String, trim: true },
6
+ }, { timestamps: true });
7
+
8
+ module.exports = mongoose.model('Position', positionSchema);
@@ -0,0 +1,14 @@
1
+ const { Router } = require('express');
2
+ const PositionController = require('./position.controller');
3
+ const { authenticate } = require('../../middleware/auth.middleware');
4
+
5
+ const router = Router();
6
+ router.use(authenticate);
7
+
8
+ router.post('/', PositionController.create);
9
+ router.get('/', PositionController.list);
10
+ router.get('/:id', PositionController.getById);
11
+ router.put('/:id', PositionController.update);
12
+ router.delete('/:id', PositionController.remove);
13
+
14
+ module.exports = router;
@@ -0,0 +1,21 @@
1
+ const Position = require('./position.model');
2
+
3
+ const PositionService = {
4
+ async create(data) {
5
+ return Position.create(data);
6
+ },
7
+ async list() {
8
+ return Position.find().sort({ posName: 1 });
9
+ },
10
+ async getById(id) {
11
+ return Position.findById(id);
12
+ },
13
+ async update(id, data) {
14
+ return Position.findByIdAndUpdate(id, data, { new: true });
15
+ },
16
+ async remove(id) {
17
+ return Position.findByIdAndDelete(id);
18
+ },
19
+ };
20
+
21
+ module.exports = PositionService;
@@ -1,37 +1,25 @@
1
- const SalaryService = require('../salary/salary.service');
1
+ const Employee = require('../employee/employee.model');
2
2
  const res_ = require('../../utils/response');
3
3
 
4
4
  const ReportsController = {
5
- async monthlyPayroll(req, res) {
5
+ async employeesOnLeave(req, res) {
6
6
  try {
7
- const { month } = req.query;
8
- const salaries = await SalaryService.getMonthlyReport(month);
7
+ const employees = await Employee.find({ empStatus: 'on leave' })
8
+ .populate('department')
9
+ .populate('position')
10
+ .sort({ 'department.departName': 1, empFirstName: 1 });
9
11
 
10
- const payroll = salaries
11
- .filter(s => s.employee)
12
- .map(s => ({
13
- _id: s._id,
14
- firstName: s.employee.firstName,
15
- lastName: s.employee.lastName,
16
- position: s.employee.position,
17
- departmentName: s.employee.department?.departmentName || '—',
18
- grossSalary: s.grossSalary,
19
- totalDeduction: s.totalDeduction,
20
- netSalary: s.netSalary,
21
- month: s.month,
22
- }));
12
+ const grouped = {};
13
+ for (const emp of employees) {
14
+ const deptName = emp.department ? emp.department.departName : 'Unassigned';
15
+ if (!grouped[deptName]) grouped[deptName] = [];
16
+ grouped[deptName].push(emp);
17
+ }
23
18
 
24
- return res_.success(res, { payroll });
25
- } catch (err) {
26
- return res_.error(res, err.message, 500);
27
- }
28
- },
29
-
30
- async departments(req, res) {
31
- try {
32
- const Department = require('../department/department.model');
33
- const departments = await Department.find().sort({ departmentCode: 1 });
34
- return res_.success(res, { departments });
19
+ return res_.success(res, {
20
+ departments: grouped,
21
+ total: employees.length,
22
+ });
35
23
  } catch (err) {
36
24
  return res_.error(res, err.message, 500);
37
25
  }
@@ -3,8 +3,8 @@ const ReportsController = require('./reports.controller');
3
3
  const { authenticate } = require('../../middleware/auth.middleware');
4
4
 
5
5
  const router = Router();
6
-
7
6
  router.use(authenticate);
8
- router.get('/payroll', ReportsController.monthlyPayroll);
7
+
8
+ router.get('/employees-on-leave', ReportsController.employeesOnLeave);
9
9
 
10
10
  module.exports = router;
@@ -1,29 +1,83 @@
1
1
  require('dotenv').config();
2
2
  const mongoose = require('mongoose');
3
+ const bcrypt = require('bcryptjs');
3
4
  const db = require('./config/db');
5
+ const env = require('./config/env');
4
6
  const Department = require('./modules/department/department.model');
5
-
6
- const departments = [
7
- { departmentCode: 'CW', departmentName: 'Carwash', grossSalary: 300000, totalDeduction: 30000 },
8
- { departmentCode: 'ST', departmentName: 'Stock', grossSalary: 200000, totalDeduction: 20000 },
9
- { departmentCode: 'MC', departmentName: 'Mechanic', grossSalary: 450000, totalDeduction: 45000 },
10
- { departmentCode: 'ADMS', departmentName: 'Administration Staff', grossSalary: 600000, totalDeduction: 60000 },
11
- ];
7
+ const Position = require('./modules/position/position.model');
8
+ const Employee = require('./modules/employee/employee.model');
9
+ const User = require('./modules/auth/user.model');
12
10
 
13
11
  async function seed() {
14
12
  try {
15
13
  await db.connect();
16
14
  console.log('Connected to MongoDB');
17
15
 
18
- for (const dept of departments) {
19
- const existing = await Department.findOne({ departmentCode: dept.departmentCode });
20
- if (!existing) {
21
- await Department.create(dept);
22
- console.log(`Created department: ${dept.departmentCode} - ${dept.departmentName}`);
23
- } else {
24
- console.log(`Department ${dept.departmentCode} already exists`);
25
- }
16
+ const departments = await Department.insertMany([
17
+ { departName: 'Sales' },
18
+ { departName: 'Warehouse' },
19
+ { departName: 'Administration' },
20
+ { departName: 'Human Resources' },
21
+ ]);
22
+ console.log(`Created ${departments.length} departments`);
23
+
24
+ const positions = await Position.insertMany([
25
+ { posName: 'Sales Representative', requiredQualification: 'Bachelor in Business' },
26
+ { posName: 'Warehouse Manager', requiredQualification: 'Diploma in Logistics' },
27
+ { posName: 'HR Officer', requiredQualification: 'Bachelor in HRM' },
28
+ { posName: 'Accountant', requiredQualification: 'Bachelor in Accounting' },
29
+ { posName: 'Cashier', requiredQualification: 'High School Diploma' },
30
+ ]);
31
+ console.log(`Created ${positions.length} positions`);
32
+
33
+ const employees = await Employee.insertMany([
34
+ {
35
+ empFirstName: 'Alice', empLastName: 'Mukamana',
36
+ empGender: 'female', empDateOfBirth: new Date('1990-05-12'),
37
+ empEmail: 'alice@dabenterprise.rw', empTelephone: '0788123456',
38
+ empAddress: 'Kigali', empHireDate: new Date('2022-01-15'),
39
+ empStatus: 'on mission', department: departments[0]._id, position: positions[0]._id,
40
+ },
41
+ {
42
+ empFirstName: 'Bob', empLastName: 'Habimana',
43
+ empGender: 'male', empDateOfBirth: new Date('1985-08-22'),
44
+ empEmail: 'bob@dabenterprise.rw', empTelephone: '0788123457',
45
+ empAddress: 'Kicukiro', empHireDate: new Date('2021-03-01'),
46
+ empStatus: 'on leave', department: departments[0]._id, position: positions[0]._id,
47
+ },
48
+ {
49
+ empFirstName: 'Claire', empLastName: 'Uwimana',
50
+ empGender: 'female', empDateOfBirth: new Date('1992-11-03'),
51
+ empEmail: 'claire@dabenterprise.rw', empTelephone: '0788123458',
52
+ empAddress: 'Remera', empHireDate: new Date('2023-06-10'),
53
+ empStatus: 'on leave', department: departments[1]._id, position: positions[1]._id,
54
+ },
55
+ {
56
+ empFirstName: 'David', empLastName: 'Niyonzima',
57
+ empGender: 'male', empDateOfBirth: new Date('1988-02-14'),
58
+ empEmail: 'david@dabenterprise.rw', empTelephone: '0788123459',
59
+ empAddress: 'Nyamirambo', empHireDate: new Date('2020-09-20'),
60
+ empStatus: 'on leave', department: departments[2]._id, position: positions[3]._id,
61
+ },
62
+ {
63
+ empFirstName: 'Grace', empLastName: 'Uwase',
64
+ empGender: 'female', empDateOfBirth: new Date('1995-07-30'),
65
+ empEmail: 'grace@dabenterprise.rw', empTelephone: '0788123460',
66
+ empAddress: 'Kanombe', empHireDate: new Date('2024-01-05'),
67
+ empStatus: 'on mission', department: departments[3]._id, position: positions[2]._id,
68
+ },
69
+ ]);
70
+ console.log(`Created ${employees.length} employees`);
71
+
72
+ const existingUser = await User.findOne({ userName: 'admin' });
73
+ if (!existingUser) {
74
+ const hashed = await bcrypt.hash('admin123', env.BCRYPT_ROUNDS);
75
+ await User.create({ userName: 'admin', password: hashed, employee: employees[0]._id });
76
+ console.log('Created user: admin / admin123');
77
+ } else {
78
+ console.log('User admin already exists');
26
79
  }
80
+
27
81
  console.log('Seed complete!');
28
82
  } catch (err) {
29
83
  console.error('Seed failed:', err.message);
@@ -1,27 +1 @@
1
- const jwt = require('jsonwebtoken');
2
- const env = require('../config/env');
3
-
4
- const PAYLOAD_FIELDS = ['id', 'email', 'role'];
5
-
6
- const pick = (obj, keys) =>
7
- keys.reduce((acc, k) => (obj[k] !== undefined ? { ...acc, [k]: obj[k] } : acc), {});
8
-
9
- const generateTokens = (user) => {
10
- const payload = pick(user, PAYLOAD_FIELDS);
11
-
12
- const accessToken = jwt.sign(payload, env.JWT_SECRET, {
13
- expiresIn: env.JWT_EXPIRES_IN,
14
- });
15
-
16
- const refreshToken = jwt.sign({ id: user.id }, env.JWT_REFRESH_SECRET, {
17
- expiresIn: env.JWT_REFRESH_EXPIRES_IN,
18
- });
19
-
20
- return { accessToken, refreshToken };
21
- };
22
-
23
- const verifyAccessToken = (token) => jwt.verify(token, env.JWT_SECRET);
24
-
25
- const verifyRefreshToken = (token) => jwt.verify(token, env.JWT_REFRESH_SECRET);
26
-
27
- module.exports = { generateTokens, verifyAccessToken, verifyRefreshToken };
1
+ module.exports = {};
@@ -0,0 +1 @@
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}.left-3{left:.75rem}.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-3{margin-bottom:.75rem}.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}.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-10{width:2.5rem}.w-16{width:4rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-5\/12{width:41.6667%}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-72{width:18rem}.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-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-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.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))}.divide-gray-50>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(249 250 251/var(--tw-divide-opacity,1))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.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-100{--tw-border-opacity:1;border-color:rgb(243 244 246/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-yellow-200{--tw-border-opacity:1;border-color:rgb(254 240 138/var(--tw-border-opacity,1))}.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-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.bg-black\/40{background-color:#0006}.bg-blue-100{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity,1))}.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-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/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-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/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-yellow-100{--tw-bg-opacity:1;background-color:rgb(254 249 195/var(--tw-bg-opacity,1))}.bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(254 252 232/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}.pl-9{padding-left:2.25rem}.pr-10{padding-right:2.5rem}.pr-3{padding-right:.75rem}.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}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.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}.capitalize{text-transform:capitalize}.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-blue-800{--tw-text-opacity:1;color:rgb(30 64 175/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-500{--tw-text-opacity:1;color:rgb(107 114 128/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-red-800{--tw-text-opacity:1;color:rgb(153 27 27/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-yellow-600{--tw-text-opacity:1;color:rgb(202 138 4/var(--tw-text-opacity,1))}.text-yellow-800{--tw-text-opacity:1;color:rgb(133 77 14/var(--tw-text-opacity,1))}.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-\[\#008A75\]:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(0 138 117/var(--tw-ring-opacity,1))}.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))}}