commit a5190a2448a463dd102ee7fef445115ff91951fc Author: Yang Li Date: Mon Apr 14 16:29:36 2025 -0400 First Commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..31a2e73 Binary files /dev/null and b/.DS_Store differ diff --git a/app/.DS_Store b/app/.DS_Store new file mode 100644 index 0000000..210ef15 Binary files /dev/null and b/app/.DS_Store differ diff --git a/app/config/.DS_Store b/app/config/.DS_Store new file mode 100644 index 0000000..b46cc2c Binary files /dev/null and b/app/config/.DS_Store differ diff --git a/app/config/auth.config.js b/app/config/auth.config.js new file mode 100644 index 0000000..ee51c57 --- /dev/null +++ b/app/config/auth.config.js @@ -0,0 +1,3 @@ +module.exports = { + secret: "worldshine-secret-key" +} \ No newline at end of file diff --git a/app/config/db.config.js b/app/config/db.config.js new file mode 100644 index 0000000..02e2978 --- /dev/null +++ b/app/config/db.config.js @@ -0,0 +1,15 @@ +const devUri = "mongodb://localhost:27017/worldshine"; +const localUri = "mongodb+srv://new-user-test:Testing123@cluster0.qkzim.mongodb.net/leapbase?retryWrites=true&w=majority"; +module.exports = { + baseUrl: "mongodb://localhost:27017/", + fileUrl: "https://worldshine.mayo.llc/files/", + database: "worldshine", + url: devUri, + + // on local enable this + // url: localUri, + // baseUrl: "mongodb+srv://new-user-test:Testing123@cluster0.qkzim.mongodb.net/", + // database: "leapbase", + // fileUrl: "http://localhost:8080/files/", + // imgBucket: "photos", +}; \ No newline at end of file diff --git a/app/controllers/auth.controller.js b/app/controllers/auth.controller.js new file mode 100644 index 0000000..df6ffe5 --- /dev/null +++ b/app/controllers/auth.controller.js @@ -0,0 +1,60 @@ +const db = require("../models"); +const Employee = db.employee; +const config = require("../config/auth.config"); + +var jwt = require("jsonwebtoken"); +var bcrypt = require("bcryptjs"); +const { splitSite } = require("../middlewares"); + +// Create and Save a new User +exports.login = (req, res) => { + var condition = {}; + const emailUsername = req.body.emailUsername; + console.log('emailUsername', emailUsername); + if (emailUsername) { + condition = { $or: [ + { email: emailUsername }, + { username: emailUsername } + ]}; + condition = splitSite.splitSiteGet(req, condition); + + Employee.find(condition) + .then(data => { + if (data && data.length > 0) { + if (data.length === 1 && bcrypt.compareSync( + req.body.password, + data[0].password + ) && data[0]?.status === 'active') { + var token = jwt.sign({id: data[0].id}, config.secret, { + expiresIn: 86400 // 24 hours + }); + res.send({ + accessToken: token, + username: data[0].username, + email: data[0].email, + roles: data[0].roles, + id: data[0].id, + name: data[0].name, + name_cn: data[0].name_cn + } ); + } else { + if (data[0].status !== 'active') { + throw(Error('User is not activated')); + } else { + throw(Error('Email or Password Is Invalid')); + } + } + } else { + throw(Error('Email or Password Is Invalid')); + } + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Email Or Password Invalid" + }); + }); + } else { + throw(Error('email or username is required')); + } +} \ No newline at end of file diff --git a/app/controllers/breakfast.controller.js b/app/controllers/breakfast.controller.js new file mode 100644 index 0000000..1f94a22 --- /dev/null +++ b/app/controllers/breakfast.controller.js @@ -0,0 +1,119 @@ +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const Breakfast = db.breakfast; + +// Create a new Breakfast Item +exports.createNewBreakfast = (req, res) => { + // Validate request + if (!req.body.customer_id) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + const site = splitSite.findSiteNumber(req); + // Create an Breakfast Item + const breakfast = new Breakfast({ + customer_id: req.body.customer_id, + customer_name: req.body.customer_name, + has_breakfast: req.body.has_breakfast, + create_by: req.body.create_by, + create_date: req.body.create_date, + edit_history: req.body.edit_history, + date: req.body.date, + site + }); + // Save breakfast Item in the database + breakfast + .save(breakfast) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Breakfast Record." + }); + }); +}; + +// Retrive all Breakfast Records from database. +exports.getAllBreakfasts = (req, res) => { + var params = req.query; + var condition = {}; + if (params.date) { + condition.date = params.date; + } + + condition = splitSite.splitSiteGet(req, condition); + Breakfast.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Breakfasts." + }); + }); +}; + +// Get One Event by Id +// exports.getEvent = (req, res) => { +// const id = req.params.id; +// CalendarEvent.findById(id) +// .then(data => { +// if (!data) +// res.status(404).send({ message: "Not found Event with id " + id }); +// else res.send(data); +// }) +// .catch(err => { +// res +// .status(500) +// .send({ message: "Error retrieving Event with id=" + id }); +// }); +// }; + +// Update a Breakfast by the id in the request +exports.updateBreakfast = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + Breakfast.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update Breakfast with id=${id}. Maybe Breakfast was not found!` + }); + } else res.send({ success: true, message: "Breakfast was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Breakfast with id=" + id + }); + }); +}; + +// Delete a Breakfast by id +exports.deleteBreakfast= (req, res) => { + const id = req.params.id; + Breakfast.findByIdAndRemove(id) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot delete Breakfast with id=${id}. Maybe Breakfast was not found!` + }); + } else { + res.send({ + message: "Breakfast was deleted successfully!" + }); + } + }) + .catch(err => { + res.status(500).send({ + message: "Could not delete Breakfast with id=" + id + }); + }); +}; \ No newline at end of file diff --git a/app/controllers/calendar-event.controller.js b/app/controllers/calendar-event.controller.js new file mode 100644 index 0000000..12a0767 --- /dev/null +++ b/app/controllers/calendar-event.controller.js @@ -0,0 +1,234 @@ +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const CalendarEvent = db.calendar_event; +const moment = require("moment-timezone"); + +// Create a new Event +exports.createCalendarEvent = (req, res) => { + // Validate request + if (!req.body.start_time) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + const site = splitSite.findSiteNumber(req); + // Create an Event + const calendarEvent = new CalendarEvent({ + title: req.body.title, + type: req.body.type, + description: req.body.description, + department: req.body.department, + notes: req.body.notes, + start_time: req.body.start_time, + stop_time: req.body.stop_time, + source_type: req.body.source_type, + source_uuid: req.body.source_uuid, + source_name: req.body.source_name, + target_type: req.body.target_type, + target_uuid: req.body.target_uuid, + target_name: req.body.target_name, + link_event_uuid: req.body.link_event_uuid, + link_event_name: req.body.link_event_name, + data: req.body.data, + color: req.body.color, + confirmed: req.body.confirmed || false, + files: req.body.files, + status: req.body.status || 'active', + signup_start_date: req.body.signup_start_date, + member_col: req.body.member_col, + new_patient: req.body.new_patient, + tags: req.body.tags, + create_by: req.body.create_by, + create_date: req.body.create_date, + edit_by: req.body.create_by, + edit_date: req.body.create_date, + youtube_video_id: req.body.youtube_video_id, + edit_history: req.body.edit_history, + site + }); + // Save event in the database + calendarEvent + .save(calendarEvent) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Calendar Event." + }); + }); +}; + +// Retrive all events from database. +exports.getAllEvents = (req, res) => { + var params = req.query; + var condition = {}; + if (params.date) { + condition['start_time'] = { + $gte: moment(params.date + ' 00:00:00:000').toDate(), + $lt: moment(params.date + ' 23:59:59.999').toDate() + }; + } + if (params.from && params.to) { + condition['start_time'] = { + $gte: moment(params.from + ' 00:00:00.000').toDate(), + $lt: moment(params.to+ ' 23:59:59.999').toDate() + }; + } + if (params.start_date && params.end_date) { + // for FullCalendar, if has start_date and end_date, format: YYYY-MM-DD + // FullCalendar gives start_date and end_date, end_date is day+1 of current view + condition['start_time'] = { + $gte: moment(parameter.start_date + 'T00:00:00:00', "America/New_York").toDate() + }; + condition['stop_time'] = { + $lt: moment(parameter.end_date + 'T00:00:00:00', "America/New_York").toDate() + }; + } + condition = splitSite.splitSiteGet(req, condition); + CalendarEvent.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Events." + }); + }); +}; + +exports.getEventsByCustomer = (req, res) => { + var params = req.query; + const name_cn = params.namecn || ''; + const name = params.name; + const id = params.id; + if (id && name) { + var ObjectId = require('mongoose').Types.ObjectId; + var objId = new ObjectId(id); + const regex = new RegExp(name_cn, 'i') // i for case insensitive + CalendarEvent.find( { $or:[ { data: {'customer':objId}}, {data: {'customer':id}}, {'target_name': name }, {'target_name': {$regex: regex}} ]}).then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Events for customers." + }); + }); + } else { + res.send({data: []}); + } +}; + +// Get One Event by Id +exports.getEvent = (req, res) => { + const id = req.params.id; + CalendarEvent.findById(id) + .then(data => { + if (!data) + res.status(404).send({ message: "Not found Event with id " + id }); + else res.send(data); + }) + .catch(err => { + res + .status(500) + .send({ message: "Error retrieving Event with id=" + id }); + }); +}; + +// Update a Event by the id in the request +exports.updateEvent = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + CalendarEvent.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update Event with id=${id}. Maybe Event was not found!` + }); + } else res.send({ success: true, message: "Event was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Event with id=" + id + }); + }); +}; + +// assign driver to the transportation Event +exports.assignTransportationToEvents = (req, res) => { + var ids = req.body.eventIds || []; + var transportationId = req.body.transportationId; + var transportationName = req.body.transportationName; + // ids.forEach(id => { + // CalendarEvent.findByIdAndUpdate(id, {link_event_uuid: transportationId, + // link_event_name: transportationName}, { useFindAndModify: false }) + // .then(data => { + // if (!data) { + // res.status(404).send({ + // message: `Cannot update Event with id=${id}. Maybe Event was not found!` + // }); + // } else res.send({ success: true, message: "Event was updated successfully." }); + // }) + // .catch(err => { + // res.status(500).send({ + // success: false, + // message: "Error updating Event with id=" + id + // }); + // }); + // }) + Promise.all(ids.map((id) => CalendarEvent.findByIdAndUpdate(id, {link_event_uuid: transportationId, + link_event_name: transportationName}, { useFindAndModify: false }))).then((data) => { + if (data) { + res.send({ success: true, message: "Event was updated successfully." }); + } + }); +} + + +// Disable an Event by the id in the request +exports.disableEvent = (req, res) => { + const id = req.params.id; + CalendarEvent.findByIdAndUpdate(id, { ...req.body, status: 'inactive'}, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update Event with id=${id}. Maybe Event was not found!` + }); + } else res.send({ success: true, message: "Event was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Event with id=" + id + }); + }); +}; + +// Delete a Event by id +exports.deleteEvent= (req, res) => { + const id = req.params.id; + CalendarEvent.findByIdAndRemove(id) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot delete Event with id=${id}. Maybe Event was not found!` + }); + } else { + res.send({ + message: "Event was deleted successfully!" + }); + } + }) + .catch(err => { + res.status(500).send({ + message: "Could not delete Event with id=" + id + }); + }); +}; \ No newline at end of file diff --git a/app/controllers/center-phone.controller.js b/app/controllers/center-phone.controller.js new file mode 100644 index 0000000..cc612ba --- /dev/null +++ b/app/controllers/center-phone.controller.js @@ -0,0 +1,87 @@ +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const CenterPhone = db.center_phone; +// Create and Save a new Center Phone +exports.createCenterPhone = (req, res) => { + // Create a Center Phone + const site = splitSite.findSiteNumber(req); + const centerPhone = new CenterPhone({ + activated: true, + phone_title: req.body.phone_title || '', + phone_number: req.body.phone_number || '', + site + }); + + // Save centerPhone in the database + centerPhone + .save(centerPhone) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Center phone." + }); + }); +}; +// Retrieve all Phones from the database. +exports.getAllCenterPhones = (req, res) => { + var params = req.query; + var condition = {}; + if (params) { + condition = splitSite.splitSiteGet(req, params); + if (params.activated) { + condition.activated = params.activated; + } + } + CenterPhone.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving phones." + }); + }); +}; +// Get One Phone by Id +exports.getCenterPhone = (req, res) => { + const id = req.params.id; + CenterPhone.findById(id) + .then(data => { + if (!data) + res.status(404).send({ message: "Not found Center Phone with id " + id }); + else res.send(data); + }) + .catch(err => { + res + .status(500) + .send({ message: "Error retrieving Center Phone with id=" + id }); + }); +}; +// Update a CenterPhone by the id in the request +exports.updateCenterPhone = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + CenterPhone.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update Center Phone with id=${id}. Maybe Center Phone was not found!` + }); + } else res.send({ success: true, message: "Center Phone was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error" + (err.message || "") + "updating Center Phone with id=" + id + }); + }); +}; + diff --git a/app/controllers/client.controller.js b/app/controllers/client.controller.js new file mode 100644 index 0000000..8e56385 --- /dev/null +++ b/app/controllers/client.controller.js @@ -0,0 +1,132 @@ +const db = require("../models"); +const Client = db.client; +var bcrypt = require("bcryptjs"); +// Create and Save a new Client +exports.createClient = (req, res) => { + // Validate request + if (!req.body.name) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + // Create a Client + const client = new Client({ + username: req.body.username || req.body.email || '', + name: req.body.name || '', + name_cn: req.body.name_cn || '', + email: req.body.email || '', + password: req.body.password ? bcrypt.hashSync(req.body.password, 8) : '', + mobile_phone: req.body.mobile_phone || '', + home_phone: req.body.home_phone || '', + phone: req.body.phone || '', + language: req.body.language || '', + status: 'active', + address: req.body.address1 || '', + address1: req.body.address2 || '', + firstname: req.body.firstname || '', + lastname: req.body.lastname || '', + birth_date: req.body.birth_date || null, + create_by: req.body.create_by || '', + create_date: new Date(), + edit_by: req.body.edit_by || '', + edit_date: new Date(), + note: req.body.note || '', + care_provider: req.body.care_provider || '', + emergency_contact: req.body.emergency_contact || '', + medicare_number: req.body.medicare_number || '', + medicaid_number: req.body.medicaid_number || '', + pharmacy: req.body.pharmacy || '', + type: req.body.type || '', + pharmacy_id: req.body.pharmacy_id || '', + pin: req.body.pin || '', + admission_date: req.body.admission_date || null, + seating: req.body.seating || '', + vehicle_no: req.body.vehicle_no || '', + caller: req.body.caller || '', + discharge_date: req.body.discharge_date || null, + placement: req.body.placement || '', + nickname: req.body.nickname || '', + groups: req.body.groups || null, + tags: req.body.tags || null, + roles: req.body.roles || null, + private_note: req.body.private_note || '', + parent_id: '5eee3552b02fac3d4acfd5ea' + }); + // Save Client in the database + client + .save(client) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Client." + }); + }); +}; + +exports.getClient = (req, res) => { + const id = req.params.id; + Client.findById(id) + .then(data => { + if (!data) + res.status(404).send({ message: "Not found client with id " + id }); + else res.send(data); + }) + .catch(err => { + res + .status(500) + .send({ message: "Error retrieving client with id=" + id }); + }); +} + +// Update a Client by the id in the request +exports.updateClient = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + if (req.body.password) { + req.body.password = bcrypt.hashSync(req.body.password, 8); + } + Client.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update client with id=${id}. Maybe Client was not found!` + }); + } else res.send({ success: true, message: "Client was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Client with id=" + id + }); + }); +}; + +// Get Client with username or email +exports.getClientsWithNameOrEmail = (req, res) => { + var params = req.query; + var condition = {}; + const nameOrEmail = params?.nameOrEmail; + if (nameOrEmail) { + condition = { $or: [ + { email: nameOrEmail }, + { name: nameOrEmail } + ]}; + } + Client.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving clients." + }); + }); +}; + diff --git a/app/controllers/customer.controller.js b/app/controllers/customer.controller.js new file mode 100644 index 0000000..0bd8954 --- /dev/null +++ b/app/controllers/customer.controller.js @@ -0,0 +1,184 @@ +const db = require("../models"); +const Customer = db.customer; +var bcrypt = require("bcryptjs"); +const { splitSite } = require("../middlewares"); +// Create and Save a new Customer +exports.createCustomer = (req, res) => { + // Validate request + if (!req.body.name) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + const site = splitSite.findSiteNumber(req); + // Create a Customer + const customer = new Customer({ + username: req.body.username || req.body.email || '', + name: req.body.name || '', + name_cn: req.body.name_cn || '', + email: req.body.email || '', + password: req.body.password ? bcrypt.hashSync(req.body.password, 8) : '', + mobile_phone: req.body.mobile_phone || '', + home_phone: req.body.home_phone || '', + phone: req.body.phone || '', + language: req.body.language || '', + status: 'active', + address1: req.body.address1 || '', + address2: req.body.address2 || '', + address3: req.body.address3 || '', + address4: req.body.address4 || '', + address5: req.body.address5 || '', + firstname: req.body.firstname || '', + lastname: req.body.lastname || '', + birth_date: req.body.birth_date || null, + create_by: req.body.create_by || '', + create_date: new Date(), + edit_by: req.body.edit_by || '', + edit_date: new Date(), + note: req.body.note || '', + care_provider: req.body.care_provider || '', + emergency_contact: req.body.emergency_contact || '', + medicare_number: req.body.medicare_number || '', + medicaid_number: req.body.medicaid_number || '', + pharmacy: req.body.pharmacy || '', + type: req.body.type || '', + avatar: req.body.avatar || '', + special_needs: req.body.special_needs || '', + pickup_status: req.body.pickup_status || '', + pharmacy_id: req.body.pharmacy_id || '', + pin: req.body.pin || '', + admission_date: req.body.admission_date || null, + seating: req.body.seating || '', + vehicle_no: req.body.vehicle_no || '', + caller: req.body.caller || '', + discharge_date: req.body.discharge_date || null, + placement: req.body.placement || '', + nickname: req.body.nickname || '', + table_id: req.body.table_id || '', + groups: req.body.groups || null, + tags: req.body.tags || null, + roles: req.body.roles || null, + apartment: req.body.apartment || '', + private_note: req.body.private_note || '', + parent_id: '5eee3552b02fac3d4acfd5ea', + site, + disability: req.body.disability || false, + weight: req.body.weight || '', + height: req.body.height || '', + gender: req.body.gender || '', + text_msg_enabled: req.body.text_msg_enabled || false + }); + // Save Customer in the database + customer + .save(customer) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Customer." + }); + }); +}; +// Retrieve all Customers from the database. +exports.getAllCustomers = (req, res) => { + var condition = {}; + condition = splitSite.splitSiteGet(req, condition); + Customer.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving customers." + }); + }); +}; +// Retrieve all Active Customer from the database. +exports.getAllActiveCustomers = (req, res) => { + var condition = { status: 'active' }; + condition = splitSite.splitSiteGet(req, condition); + Customer.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Customers." + }); + }); +}; +// Get One Customer by Id +exports.getCustomer = (req, res) => { + const id = req.params.id; + Customer.findById(id) + .then(data => { + if (!data) + res.status(404).send({ message: "Not found Customer with id " + id }); + else res.send(data); + }) + .catch(err => { + res + .status(500) + .send({ message: "Error retrieving Customer with id=" + id }); + }); +}; +// Update a Customer by the id in the request +exports.updateCustomer = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + if (req.body.password) { + req.body.password = bcrypt.hashSync(req.body.password, 8); + } + Customer.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update customer with id=${id}. Maybe Customer was not found!` + }); + } else res.send({ success: true, message: "Customer was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Customer with id=" + id + }); + }); +}; +// Soft Delete a Customer with the specified id in the request +exports.deactivateCustomer = (req, res) => { + +}; +// Delete a Customer by id +exports.deleteCustomer = (req, res) => { + +}; +// Get Customer with username or email +exports.getCustomersWithNameOrEmail = (req, res) => { + var params = req.query; + var condition = {}; + const nameOrEmail = params?.nameOrEmail; + if (nameOrEmail) { + condition = { $or: [ + { email: nameOrEmail }, + { name: nameOrEmail } + ]}; + } + condition = splitSite.splitSiteGet(req, condition); + Customer.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving customers." + }); + }); +}; diff --git a/app/controllers/doctemplate.controller.js b/app/controllers/doctemplate.controller.js new file mode 100644 index 0000000..156b6d5 --- /dev/null +++ b/app/controllers/doctemplate.controller.js @@ -0,0 +1,470 @@ +// const axios = require("axios"); +// const { splitSite } = require("../middlewares"); +// const db = require("../models"); +// const DocTemplate = db.doctemplate +// var multer = require('multer'); +// var PizZip = require('pizzip'); +// var Docxtemplater = require('docxtemplater'); +// var fs = require('fs'); +// const path = require('path'); +// // const docxConverter = require('docx-pdf'); +// var libre = require('libreoffice-convert'); + + +// exports.createPDFFromDocTemplateName = (req, res) => { +// console.log('what', __dirname.replace('/controllers', '')); +// var inputData = req.query.inputData; +// var docTemplateName = req.query.docTemplateName; +// // var transportationId = req.body.transportationId;d +// // var transportationName = req.body.transportationName; +// DocTemplate.find({name: docTemplateName}).then((data) => { +// try { +// if (data && data.length > 0) { +// var docTemplate = data[0] || {}; +// var templateDoc = `${__dirname.replace('/controllers', '')}${docTemplate?.file[0]?.url}`; +// var outputFileRandom = Math.ceil(Math.random() * 100000000); +// var outputFile = `/tmp/${docTemplate.name}_${outputFileRandom}.docx`; +// var content = fs.readFileSync(templateDoc, 'binary'); +// var zip = new PizZip(content); +// var doc = new Docxtemplater(zip, { +// paragraphLoop: true, +// linebreaks: true, +// }); + + + +// try { +// // render the document (replace all occurences of {first_name} by John, {last_name} by Doe, ...) +// doc.render(JSON.parse(inputData)); +// } catch (error) { +// // The error thrown here contains additional information when logged with JSON.stringify (it contains a properties object containing all suberrors). +// function replaceErrors(key, value) { +// if (value instanceof Error) { +// return Object.getOwnPropertyNames(value).reduce(function(error, key) { +// error[key] = value[key]; +// return error; +// }, {}); +// } +// return value; +// } +// console.log(JSON.stringify({error: error}, replaceErrors)); +// if (error.properties && error.properties.errors instanceof Array) { +// const errorMessages = error.properties.errors.map(function (error) { +// return error.properties.explanation; +// }).join("\n"); +// console.log('errorMessages', errorMessages); +// // errorMessages is a humanly readable message looking like this : +// // 'The tag beginning with "foobar" is unopened' +// } +// // callback && callback(error, null, { message:'error in creating doc from template' }); +// } +// // save doc to output file +// var buf = doc.getZip().generate({type: 'nodebuffer'}); +// // buf is a nodejs buffer, you can either write it to a file or do anything else with it. +// fs.writeFileSync(outputFile, buf); +// var outputFilename = outputFile || {}; +// var outputPdfPath = outputFilename.substr(0, outputFilename.length - 5) + '.pdf'; +// console.log('outputPDF:', outputPdfPath); +// var extend = 'pdf'; +// // Read file +// // var outputFilename = outputFile || {}; +// // console.log('filename', `/tmp/${docTemplate.name}_${outputFileRandom}.docx`) +// // docxConverter(path.resolve(`/tmp/${docTemplate.name}_${outputFileRandom}.docx`), path.resolve(`/tmp/${docTemplate.name}_${outputFileRandom}.pdf`), (err, result) => { +// // if (err) console.log(err); +// // else { +// // console.log('res', res); +// // res.download(result, (error) => { +// // console.log('Download PDF error') +// // }) +// // } +// // }); + +// var infile = fs.readFileSync(outputFile); +// // Convert it to pdf format with undefined filter (see Libreoffice doc about filter) +// try { +// libre.convert(infile, extend, undefined, (err, done) => { +// if (err) { +// console.log('Error converting file:', err); +// } +// // Here in done you have pdf file which you can save or transfer in another stream +// fs.writeFileSync(outputPdfPath, done); +// console.log('Conver docx to pdf, Done.'); +// res.download(outputPdfPath, function(error) { +// if (error) { +// console.log('Error in sending download file ${outputPdfPath}'); +// } +// }); +// }); +// } catch (e) { +// console.log(e); +// } + +// } else { +// res.status(404).send({ +// success: false, +// message: "Docs Template doesn't exist" +// }) +// } +// } catch(e) { +// console.log(e); +// } +// }).catch(err => { +// res.status(500).send({ +// success: false, +// message: "Error Geting docs" +// }); +// }); +// } + +// exports.createDocFromDocTemplateName = (req, res) => { +// console.log('what', __dirname.replace('/controllers', '')); +// // var inputData = req.query.inputData; +// var inputData = req.body.inputData; +// var docTemplateName = req.body.docTemplateName; +// DocTemplate.find({name: docTemplateName}).then((data) => { +// console.log(data); +// try { +// if (data && data.length > 0) { +// var docTemplate = data[0] || {}; +// var templateDoc = `${__dirname.replace('/controllers', '')}${docTemplate?.file[0]?.url}`; +// var outputFileRandom = Math.ceil(Math.random() * 100000000); +// var outputFile = `/tmp/${docTemplate.name}_${outputFileRandom}.docx`; +// var content = fs.readFileSync(templateDoc, 'binary'); +// var zip = new PizZip(content); +// var doc = new Docxtemplater(zip, { +// paragraphLoop: true, +// linebreaks: true, +// }); + +// try { +// // render the document (replace all occurences of {first_name} by John, {last_name} by Doe, ...) +// doc.render(JSON.parse(inputData)); +// } catch (error) { +// // The error thrown here contains additional information when logged with JSON.stringify (it contains a properties object containing all suberrors). +// function replaceErrors(key, value) { +// if (value instanceof Error) { +// return Object.getOwnPropertyNames(value).reduce(function(error, key) { +// error[key] = value[key]; +// return error; +// }, {}); +// } +// return value; +// } +// console.log(JSON.stringify({error: error}, replaceErrors)); +// if (error.properties && error.properties.errors instanceof Array) { +// const errorMessages = error.properties.errors.map(function (error) { +// return error.properties.explanation; +// }).join("\n"); +// console.log('errorMessages', errorMessages); +// // errorMessages is a humanly readable message looking like this : +// // 'The tag beginning with "foobar" is unopened' +// } +// // callback && callback(error, null, { message:'error in creating doc from template' }); +// } +// // save doc to output file +// var buf = doc.getZip().generate({type: 'nodebuffer'}); +// // buf is a nodejs buffer, you can either write it to a file or do anything else with it. +// fs.writeFileSync(outputFile, buf); +// var outputFilename = outputFile || {}; + +// res.send(outputFilename, (error) => { +// if (error) { +// console.log('Error in downloading excel') +// } +// }); + +// console.log('Doc Download Completes') + +// } else { +// res.status(404).send({ +// success: false, +// message: "Docs Template doesn't exist" +// }) +// } +// } catch(e) { +// console.log(e); +// } +// }).catch(err => { +// res.status(500).send({ +// success: false, +// message: "Error Geting docs" +// }); +// }); +// // ids.forEach(id => { +// // CalendarEvent.findByIdAndUpdate(id, {link_event_uuid: transportationId, +// // link_event_name: transportationName}, { useFindAndModify: false }) +// // .then(data => { +// // if (!data) { +// // res.status(404).send({ +// // message: `Cannot update Event with id=${id}. Maybe Event was not found!` +// // }); +// // } else res.send({ success: true, message: "Event was updated successfully." }); +// // }) +// // .catch(err => { +// // res.status(500).send({ +// // success: false, +// // message: "Error updating Event with id=" + id +// // }); +// // }); +// // }) +// } + + + +const axios = require("axios"); +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const DocTemplate = db.doctemplate; +const Event = db.calendar_event; +var multer = require('multer'); +var PizZip = require('pizzip'); +var Docxtemplater = require('docxtemplater'); +var fs = require('fs'); +const path = require('path'); +// const docxConverter = require('docx-pdf'); +var libre = require('libreoffice-convert'); +const moment = require("moment-timezone"); + + +exports.createPDFFromDocTemplateName = (req, res) => { + console.log('what', __dirname.replace('/controllers', '')); + var inputData = JSON.parse(req.query.inputData); + var docTemplateName = req.query.docTemplateName; + const eventIds =inputData?.eventIds; + Event.find({ '_id': { $in: eventIds } }).then((events) => { + const docData = { + events: [] + }; + events.forEach((event) => { + docData.events.push({ + event_time: moment(event.start_time).format('hh:mm A MM/DD/YYYY dddd'), + client_name: event.data.client_name || '', + client_status: '会员', + //client_dob: event.data.client_birth_date || '', + //client_pcp: event.data.client_pcp || '', + //client_pharmacy: event.data.client_pharmacy || '', + client_seating: event.data.client_seating || '', + doctor_name: event.data.resource_name || '', + //doctor_phone: event.data.resource_phone || '', + doctor_contact: event.data.resource_contact || '', + doctor_address: event.data.resource_address || '', + //title: event.title || '', + description: event.description || '', + interpreter: event.data.interpreter || '', + //fasting: event.data.fasting || '', + //confirmed: event.data.confirmed, + //new_patient: event.data.new_patient, + //doc_order: event.data.doc_order, + //need_id: event.data.need_id, + //need_med_list: event.data.need_med_list, + reason: event.data.reason || '', + other: event.data.other || '' + }); + }); + // var transportationId = req.body.transportationId;d + // var transportationName = req.body.transportationName; + DocTemplate.find({name: docTemplateName}).then((data) => { + try { + if (data && data.length > 0) { + var docTemplate = data[0] || {}; + var templateDoc = `${__dirname.replace('/controllers', '')}${docTemplate?.file[0]?.url}`; + var outputFileRandom = Math.ceil(Math.random() * 100000000); + var outputFile = `/tmp/${docTemplate.name}_${outputFileRandom}.docx`; + var content = fs.readFileSync(templateDoc, 'binary'); + var zip = new PizZip(content); + var doc = new Docxtemplater(zip, { + paragraphLoop: true, + linebreaks: true, + }); + + try { + // render the document (replace all occurences of {first_name} by John, {last_name} by Doe, ...) + doc.render(docData); + } catch (error) { + // The error thrown here contains additional information when logged with JSON.stringify (it contains a properties object containing all suberrors). + function replaceErrors(key, value) { + if (value instanceof Error) { + return Object.getOwnPropertyNames(value).reduce(function(error, key) { + error[key] = value[key]; + return error; + }, {}); + } + return value; + } + console.log(JSON.stringify({error: error}, replaceErrors)); + if (error.properties && error.properties.errors instanceof Array) { + const errorMessages = error.properties.errors.map(function (error) { + return error.properties.explanation; + }).join("\n"); + console.log('errorMessages', errorMessages); + // errorMessages is a humanly readable message looking like this : + // 'The tag beginning with "foobar" is unopened' + } + // callback && callback(error, null, { message:'error in creating doc from template' }); + } + // save doc to output file + var buf = doc.getZip().generate({type: 'nodebuffer'}); + // buf is a nodejs buffer, you can either write it to a file or do anything else with it. + fs.writeFileSync(outputFile, buf); + var outputFilename = outputFile || {}; + var outputPdfPath = outputFilename.substr(0, outputFilename.length - 5) + '.pdf'; + console.log('outputPDF:', outputPdfPath); + var extend = 'pdf'; + // Read file + // var outputFilename = outputFile || {}; + // console.log('filename', `/tmp/${docTemplate.name}_${outputFileRandom}.docx`) + // docxConverter(path.resolve(`/tmp/${docTemplate.name}_${outputFileRandom}.docx`), path.resolve(`/tmp/${docTemplate.name}_${outputFileRandom}.pdf`), (err, result) => { + // if (err) console.log(err); + // else { + // console.log('res', res); + // res.download(result, (error) => { + // console.log('Download PDF error') + // }) + // } + // }); + + var infile = fs.readFileSync(outputFile); + // Convert it to pdf format with undefined filter (see Libreoffice doc about filter) + try { + libre.convert(infile, extend, undefined, (err, done) => { + if (err) { + console.log('Error converting file:', err); + } + // Here in done you have pdf file which you can save or transfer in another stream + fs.writeFileSync(outputPdfPath, done); + console.log('Conver docx to pdf, Done.'); + res.download(outputPdfPath, function(error) { + if (error) { + console.log('Error in sending download file ${outputPdfPath}'); + } + }); + }); + } catch (e) { + console.log(e); + } + + } else { + res.status(404).send({ + success: false, + message: "Docs Template doesn't exist" + }) + } + } catch(e) { + console.log(e); + } + }).catch(err => { + res.status(500).send({ + success: false, + message: "Error Geting docs" + }); + }); + }) +} + +exports.createDocFromDocTemplateName = (req, res) => { + console.log('what', __dirname.replace('/controllers', '')); + var inputData = JSON.parse(req.query.inputData); + const eventIds = inputData?.eventIds; + var docTemplateName = req.query.docTemplateName; + Event.find({ '_id': { $in: eventIds } }).then((events) => { + const docData = { + events: [] + }; + events.forEach((event) => { + docData.events.push({ + event_time: moment(event.start_time).format('hh:mm A MM/DD/YYYY dddd'), + client_name: event.data.client_name || '', + client_status: '会员', + //client_dob: event.data.client_birth_date || '', + //client_pcp: event.data.client_pcp || '', + //client_pharmacy: event.data.client_pharmacy || '', + client_seating: event.data.client_seating || '', + doctor_name: event.data.resource_name || '', + //doctor_phone: event.data.resource_phone || '', + doctor_contact: event.data.resource_contact || '', + doctor_address: event.data.resource_address || '', + //title: event.title || '', + description: event.description || '', + interpreter: event.data.interpreter || '', + //fasting: event.data.fasting || '', + //confirmed: event.data.confirmed, + //new_patient: event.data.new_patient, + //doc_order: event.data.doc_order, + //need_id: event.data.need_id, + //need_med_list: event.data.need_med_list, + reason: event.data.reason || '', + other: event.data.other || '' + }); + }); + + DocTemplate.find({name: docTemplateName}).then((data) => { + try { + if (data && data.length > 0) { + var docTemplate = data[0] || {}; + var templateDoc = `${__dirname.replace('/controllers', '')}${docTemplate?.file[0]?.url}`; + var outputFileRandom = Math.ceil(Math.random() * 100000000); + var outputFile = `/tmp/${docTemplate.name}_${outputFileRandom}.docx`; + var content = fs.readFileSync(templateDoc, 'binary'); + var zip = new PizZip(content); + var doc = new Docxtemplater(zip, { + paragraphLoop: true, + linebreaks: true, + }); + + try { + // render the document (replace all occurences of {first_name} by John, {last_name} by Doe, ...) + doc.render(docData); + } catch (error) { + // The error thrown here contains additional information when logged with JSON.stringify (it contains a properties object containing all suberrors). + function replaceErrors(key, value) { + if (value instanceof Error) { + return Object.getOwnPropertyNames(value).reduce(function(error, key) { + error[key] = value[key]; + return error; + }, {}); + } + return value; + } + console.log(JSON.stringify({error: error}, replaceErrors)); + if (error.properties && error.properties.errors instanceof Array) { + const errorMessages = error.properties.errors.map(function (error) { + return error.properties.explanation; + }).join("\n"); + console.log('errorMessages', errorMessages); + // errorMessages is a humanly readable message looking like this : + // 'The tag beginning with "foobar" is unopened' + } + // callback && callback(error, null, { message:'error in creating doc from template' }); + } + // save doc to output file + var buf = doc.getZip().generate({type: 'nodebuffer'}); + // buf is a nodejs buffer, you can either write it to a file or do anything else with it. + fs.writeFileSync(outputFile, buf); + var outputFilename = outputFile || {}; + + res.download(outputFilename, (error) => { + if (error) { + console.log('Error in downloading excel') + } + }); + + console.log('Doc Download Completes') + + } else { + res.status(404).send({ + success: false, + message: "Docs Template doesn't exist" + }) + } + } catch(e) { + console.log(e); + } + }).catch(err => { + res.status(500).send({ + success: false, + message: "Error Geting docs" + }); + }); + }); +} + diff --git a/app/controllers/employee.controller.js b/app/controllers/employee.controller.js new file mode 100644 index 0000000..5aea528 --- /dev/null +++ b/app/controllers/employee.controller.js @@ -0,0 +1,152 @@ +const db = require("../models"); +const Employee = db.employee; + +var bcrypt = require("bcryptjs"); +const { splitSite } = require("../middlewares"); +// Create and Save a new Employee (driver, distributor, admin) +exports.createEmployee = (req, res) => { + // Validate request + if (!req.body.username) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + const site = splitSite.findSiteNumber(req); + // Create a Employee + const employee = new Employee({ + username: req.body.username || req.body.email || '', + name: req.body.name || '', + name_cn: req.body.name_cn || '', + email: req.body.email || '', + password: req.body.password ? bcrypt.hashSync(req.body.password, 8) : '', + roles: req.body.roles || [], + mobile_phone: req.body.mobile_phone || '', + phone: req.body.phone || '', + home_phone: req.body.home_phone || '', + language: req.body.language || '', + employment_status: req.body.employment_status || '', + status: req.body.status || 'active', + address: req.body.address || '', + title: req.body.title || '', + title_cn: req.body.title_cn || '', + firstname: req.body.firstname || '', + lastname: req.body.lastname || '', + department: req.body.department || '', + birth_date: req.body.birth_date || null, + driver_capacity: req.body.driver_capacity || null, + date_hired: req.body.date_hired || null, + create_by: req.body.create_by || '', + create_date: new Date(), + edit_by: req.body.edit_by || '', + edit_date: new Date(), + note: req.body.note || '', + tags: req.body.tags || [], + fetch_route_time: req.body.fetch_route_time || null, + site + }); + // Save Employee in the database + employee + .save(employee) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Employee." + }); + }); +}; +// Retrieve all Employee from the database. +exports.getAllEmployees = (req, res) => { + var params = req.query; + var condition = {}; + condition = splitSite.splitSiteGet(req, condition); + if (params.status) { + condition.status = params.status; + } + if (params.role) { + condition.roles = params.role; + } + Employee.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving employees." + }); + }); +}; +// Retrieve all Active Employee from the database. +exports.getAllActiveEmployees = (req, res) => { + var params = req.query; + var condition = { status: 'active' }; + condition = splitSite.splitSiteGet(req, condition); + if (params.roles) { + condition.roles = params.roles; + } + Employee.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving employees." + }); + }); +}; +// Get One Employee by Id +exports.getEmployee = (req, res) => { + const id = req.params.id; + Employee.findById(id) + .then(data => { + if (!data) + res.status(404).send({ message: "Not found Employee with id " + id }); + else res.send(data); + }) + .catch(err => { + res + .status(500) + .send({ message: "Error retrieving Employee with id=" + id }); + }); +}; +// Update a Employee by the id in the request +exports.updateEmployee = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + if (req.body.password) { + req.body.password = bcrypt.hashSync(req.body.password, 8); + } + Employee.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update employee with id=${id}. Maybe Employee was not found!` + }); + } else res.send({ success: true, message: "Employee was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Employee with id=" + id + }); + }); +}; +// Soft Delete a Employee with the specified id in the request +exports.deactivateEmployee = (req, res) => { + +}; +// Delete a Employee by id +exports.deleteEmployee = (req, res) => { + +}; +// Get Employees with username or email +exports.getEmployeesWithUsernameOrEmail = (req, res) => { + +}; diff --git a/app/controllers/event-request.controller.js b/app/controllers/event-request.controller.js new file mode 100644 index 0000000..9388042 --- /dev/null +++ b/app/controllers/event-request.controller.js @@ -0,0 +1,109 @@ +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const EventRequest = db.event_request; + +// Create a new Event Request Item +exports.createEventRequest = (req, res) => { + // Validate request + if (!req.body.customer_id) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + const site = splitSite.findSiteNumber(req); + // Create an eventRequest Item + const eventRequest = new EventRequest({ + customer_id: req.body.customer_id, + customer_display: req.body.customer_display, + upload: req.body.upload, + resource: req.body.resource, + resource_display: req.body.resource_display, + source: req.body.source, + type: req.body.type, + status: req.body.status || 'active', + symptom: req.body.symptom, + transportation: req.body.transportation, + np: req.body.np, + notes: req.body.notes || [], + create_by: req.body.create_by, + create_date: req.body.create_date, + edit_history: req.body.edit_history, + site + }); + // Save eventRequest Item in the database + eventRequest + .save(eventRequest) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the event request Record." + }); + }); +}; + +// Retrive all eventRequest Records from database. +exports.getAllEventRequests = (req, res) => { + var params = req.query; + var condition = {}; + + condition = splitSite.splitSiteGet(req, condition); + EventRequest.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Event Requests." + }); + }); +}; + + +// Update an Event Request Item by the id in the request +exports.updateRequestItem = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + EventRequest.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update Event Request with id=${id}. Maybe Event Request was not found!` + }); + } else res.send({ success: true, message: "Event Request was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Event Request with id=" + id + }); + }); +}; + +// Delete an Event Request by id +exports.deleteEventRequest = (req, res) => { + const id = req.params.id; + EventRequest.findByIdAndRemove(id) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot delete Event Request with id=${id}. Maybe Event Request was not found!` + }); + } else { + res.send({ + message: "Event Request was deleted successfully!" + }); + } + }) + .catch(err => { + res.status(500).send({ + message: "Could not delete Event Request with id=" + id + }); + }); +}; \ No newline at end of file diff --git a/app/controllers/lunch.controller.js b/app/controllers/lunch.controller.js new file mode 100644 index 0000000..aa72949 --- /dev/null +++ b/app/controllers/lunch.controller.js @@ -0,0 +1,119 @@ +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const Lunch = db.lunch; + +// Create a new Lunch Item +exports.createNewLunch = (req, res) => { + // Validate request + if (!req.body.customer_id) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + const site = splitSite.findSiteNumber(req); + // Create an Lunch Item + const lunch = new Lunch({ + customer_id: req.body.customer_id, + customer_name: req.body.customer_name, + has_lunch: req.body.has_lunch, + create_by: req.body.create_by, + create_date: req.body.create_date, + edit_history: req.body.edit_history, + date: req.body.date, + site + }); + // Save lunch Item in the database + lunch + .save(lunch) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Lunch Record." + }); + }); +}; + +// Retrive all Lunch Records from database. +exports.getAllLunches = (req, res) => { + var params = req.query; + var condition = {}; + if (params.date) { + condition.date = params.date; + } + + condition = splitSite.splitSiteGet(req, condition); + Lunch.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Lunches." + }); + }); +}; + +// Get One Event by Id +// exports.getEvent = (req, res) => { +// const id = req.params.id; +// CalendarEvent.findById(id) +// .then(data => { +// if (!data) +// res.status(404).send({ message: "Not found Event with id " + id }); +// else res.send(data); +// }) +// .catch(err => { +// res +// .status(500) +// .send({ message: "Error retrieving Event with id=" + id }); +// }); +// }; + +// Update a Lunch by the id in the request +exports.updateLunch = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + Lunch.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update Lunch with id=${id}. Maybe Lunch was not found!` + }); + } else res.send({ success: true, message: "Lunch was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Lunch with id=" + id + }); + }); +}; + +// Delete a Lunch by id +exports.deleteLunch= (req, res) => { + const id = req.params.id; + Lunch.findByIdAndRemove(id) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot delete Lunch with id=${id}. Maybe Lunch was not found!` + }); + } else { + res.send({ + message: "Lunch was deleted successfully!" + }); + } + }) + .catch(err => { + res.status(500).send({ + message: "Could not delete Lunch with id=" + id + }); + }); +}; \ No newline at end of file diff --git a/app/controllers/message-token.controller.js b/app/controllers/message-token.controller.js new file mode 100644 index 0000000..7fc83e9 --- /dev/null +++ b/app/controllers/message-token.controller.js @@ -0,0 +1,63 @@ +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const MessageToken = db.message_token; +exports.createMessageToken = (req, res) => { + const site = splitSite.findSiteNumber(req); + // Create a MessageToken + const messageToken = new MessageToken({ + message_token: req.body.message_token || '', + site + }); + + // Save MessageToken in the database + messageToken + .save(messageToken) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Message Token." + }); + }); +}; +// Retrieve all MessageTokens from the database. +exports.getAllMessageTokens = (req, res) => { + var condition = {}; + MessageToken.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving messageTokens." + }); + }); +}; + +// Update a MessageTOken by the id in the request +exports.updateMessageToken = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + MessageToken.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update MessageToken with id=${id}. Maybe MessageToken was not found!` + }); + } else res.send({ success: true, message: "MessageToken was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating MessageToken with id=" + id + }); + }); +}; + diff --git a/app/controllers/message.controller.js b/app/controllers/message.controller.js new file mode 100644 index 0000000..1df22b9 --- /dev/null +++ b/app/controllers/message.controller.js @@ -0,0 +1,191 @@ +const axios = require("axios"); +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const Message = db.message; +const MessageToken = db.message_token; +const SentMessage = db.sent_message; + +exports.createMessage = (req, res) => { + // Validate request + if (!req.body.message_group) { + res.status(400).send({ message: "Message Group can not be empty!" }); + return; + } + const site = splitSite.findSiteNumber(req); + // Create a Message + const message = new Message({ + message_group: req.body.message_group || 0, + message_name: req.body.message_name || '', + message_title: req.body.message_title || '', + message_body: req.body.message_body || '', + language: req.body.language || '', + site + }); + + // Save Message in the database + message + .save(message) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Message." + }); + }); +}; +// Retrieve all Messages from the database. +exports.getAllMessages = (req, res) => { + var condition = {}; + Message.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving messages." + }); + }); +}; + +// Retrieve messages By group and language +exports.getMessagesByGroupAndLanguage= (req, res) => { + const params = req.query; + const message_group = params?.message_group; + const language = params?.language; + var condition = { message_group, language }; + Message.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Messages with Date and Type." + }); + }); +}; + +// Get One Message by Id +exports.getMessage = (req, res) => { + const id = req.params.id; + Message.findById(id) + .then(data => { + if (!data) + res.status(404).send({ message: "Not found Message with id " + id }); + else res.send(data); + }) + .catch(err => { + res + .status(500) + .send({ message: "Error retrieving Message with id=" + id }); + }); +}; +// Update a Message by the id in the request +exports.updateMessage = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + Message.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update Message with id=${id}. Maybe Message was not found!` + }); + } else res.send({ success: true, message: "Message was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Message with id=" + id + }); + }); +}; + + +// exports.sendMessage = async (req, res) => { +// console.log(req.body); +// if (!req.body) { +// return res.status(400).send({ +// message: "Data to update can not be empty!" +// }); +// } +// const authToken = await MessageToken.find({}); +// if (authToken && authToken.length > 0) { +// const token = authToken[0]?.message_token; +// console.log('token', token); +// await axios.post('https://api-app2.simpletexting.com/v2/api/messages', req.body, { +// headers: { +// "Authorization": `Bearer ${token}`, +// "Content-Type": "application/json", +// }, +// }).then(() => { +// res.send({success: true, message: "Message Sent Correctly"}) +// }).catch(err => console.log(err)); +// } +// } + +// Retrieve all Sent Messages from the database. +exports.getAllSentMessages = (req, res) => { + var condition = {}; + condition = splitSite.splitSiteGet(req, condition); + SentMessage.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving sent messages." + }); + }); +}; + +exports.sendMessage = async (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + const messages = req.body.messages; + + for(const message of messages) { + try { + await axios.post('https://rest.textmagic.com/api/v2/messages', { + text: message?.text, + phones: message?.contactPhone + }, { + headers: { + "X-TM-Username": "oauthyhma", + "X-TM-Key": "Rin866o0cMMAJXkCiIyYd2pw9HSeGo" + } + }) + const site = splitSite.findSiteNumber(req); + const sentMessage = new SentMessage({ + from_type: 'center', + from: message.from || 'center', + to_type: 'client', + to: message.contactPhone || '', + department: message.department || '', + content: message.text || '', + status: 'active', + create_by: 'admin', + create_date: new Date(), + site + }); + await sentMessage.save(sentMessage); + await sleep(200); + } catch (err) { + console.log(err); + } + + } + res.send({success: true, message: "Message Sent Correctly"}); +} + diff --git a/app/controllers/report.controller.js b/app/controllers/report.controller.js new file mode 100644 index 0000000..95b418f --- /dev/null +++ b/app/controllers/report.controller.js @@ -0,0 +1,118 @@ +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const Report = db.report; +exports.createReport = (req, res) => { + // Validate request + if (!req.body.data) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + const site = splitSite.findSiteNumber(req); + // Create a Report + const report = new Report({ + date: req.body.date || '', + type: req.body.type || '', + route_id: req.body.route_id || '', + driver_name: req.body.driver_name || '', + route_name: req.body.route_name || '', + data: req.body.data || [], + head: req.body.head || [], + chinese_head: req.body.chinese_head || [], + checklist_result: req.body.checklist_result || [], + vehicle_number: req.body.vehicle_number || null, + site + }); + + // Save Report in the database + report + .save(report) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Report." + }); + }); +}; +// Retrieve all Reports from the database. +exports.getAllReports = (req, res) => { + var condition = {}; + condition = splitSite.splitSiteGet(req, condition); + Report.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving reports." + }); + }); +}; +// Retrieve all Active Reports By Date and Type (Admin Reports). +exports.getReportsByDateAndType = (req, res) => { + const params = req.query; + const date = params?.date; + const type = params?.type; + var condition = { date, type }; + condition = splitSite.splitSiteGet(req, condition); + Report.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Reports with Date and Type." + }); + }); +}; +// Retrieve reports By RouteId and Type (Senior Route report) +exports.getReportsByRouteIdAndType = (req, res) => { + const params = req.query; + const route_id = params?.route_id; + const type = params?.type; + var condition = { route_id, type }; + condition = splitSite.splitSiteGet(req, condition); + Report.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Reports with Date and Type." + }); + }); +}; + +// Get One Report by Id +exports.getReport = (req, res) => { + +}; +// Update a Report by the id in the request +exports.updateReport = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + Report.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update Report with id=${id}. Maybe Report was not found!` + }); + } else res.send({ success: true, message: "Report was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Report with id=" + id + }); + }); +}; + diff --git a/app/controllers/resource.controller.js b/app/controllers/resource.controller.js new file mode 100644 index 0000000..d62bb15 --- /dev/null +++ b/app/controllers/resource.controller.js @@ -0,0 +1,152 @@ +const db = require("../models"); +const Resource = db.resource; +const { splitSite } = require("../middlewares"); +// Create and Save a new Resource +exports.createResource = (req, res) => { + // Validate request + if (!req.body.name) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + const site = splitSite.findSiteNumber(req); + const resource = new Resource({ + name: req.body.name, + name_original: req.body.name_original, + name_branch: req.body.name_branch, + specialty: req.body.specialty, + type: req.body.type, // value may be ['doctor', 'pharmacy' or 'other'] + color: req.body.color, + address: req.body.address, + city: req.body.city, + state: req.body.state, + zipcode: req.body.zipcode, + phone: req.body.phone, + status: req.body.status || 'active', // value might be ['active', 'inactive'] + create_by: req.body.create_by, + create_date: req.body.create_date || new Date(), + parent_id: req.body.parent_id, + ext_id: req.body.ext_id, + category: req.body.category, + description: req.body.description, + contact: req.body.contact, + fax: req.body.fax, + note: req.body.note, + data: req.body.data, + edit_by: req.body.edit_by, + edit_date: req.body.edit_date || new Date(), + images: req.body.images, + edit_history: req.body.edit_history, + site + }); + // Save Resource in the database + resource + .save(resource) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Resource" + }); + }); +}; +// Retrieve all Resources from the database. +exports.getAllResources = (req, res) => { + var params = req.query; + var condition = {}; + if (params) { + if (params.type) { + condition.type = params.type; + } + } + Resource.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving resources." + }); + }); +}; +// Get One Resource by Id +exports.getResource = (req, res) => { + const id = req.params.id; + Resource.findById(id) + .then(data => { + if (!data) + res.status(404).send({ message: "Not found Resource with id " + id }); + else res.send(data); + }) + .catch(err => { + res + .status(500) + .send({ message: "Error retrieving Resource with id=" + id }); + }); +}; + +// Update a Resource by the id in the request +exports.updateResource = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + Resource.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update Resource with id=${id}. Maybe Resource was not found!` + }); + } else res.send({ success: true, message: "Resource was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Resource with id=" + id + }); + }); +}; +// Disable an Resource by the id in the request +exports.disableResource = (req, res) => { + const id = req.params.id; + Resource.findByIdAndUpdate(id, { ...req.body, status: 'inactive'}, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update Resource with id=${id}. Maybe Resource was not found!` + }); + } else res.send({ success: true, message: "Resource was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Resource with id=" + id + }); + }); +}; + +// Delete a Resource by id +exports.deleteResource= (req, res) => { + const id = req.params.id; + Resource.findByIdAndRemove(id) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot delete Resource with id=${id}. Maybe Resource was not found!` + }); + } else { + res.send({ + message: "Resource was deleted successfully!" + }); + } + }) + .catch(err => { + res.status(500).send({ + message: "Could not delete Resource with id=" + id + }); + }); +}; \ No newline at end of file diff --git a/app/controllers/route-path-template.controller.js b/app/controllers/route-path-template.controller.js new file mode 100644 index 0000000..6a5cc83 --- /dev/null +++ b/app/controllers/route-path-template.controller.js @@ -0,0 +1,99 @@ +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const RoutePathTemplate = db.route_path_template; +exports.createRoutePathTemplate = (req, res) => { + // Validate request + if (!req.body.name) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + const site = splitSite.findSiteNumber(req); + // Create a Route Template + const routePathTemplate = new RoutePathTemplate({ + name: req.body.name, + vehicle: req.body.vehicle, + driver: req.body.driver, + type: req.body.type, + route_customer_list: req.body.route_customer_list || [], + status: req.body.status || 'active', + site + }); + // Save Route Template in the database + routePathTemplate + .save(routePathTemplate) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Route Template." + }); + }); +}; +// Retrieve all Routes Templates from the database. +exports.getAllRoutesTemplates = (req, res) => { + var condition = {}; + condition = splitSite.splitSiteGet(req, condition); + RoutePathTemplate.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Routes." + }); + }); +}; + +// Get One Route Template by Id +exports.getRouteTemplate = (req, res) => { + +}; + +// Update a Route Template by the id in the request +exports.updateRouteTemplate = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + RoutePathTemplate.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update Route Template with id=${id}. Maybe Route Template was not found!` + }); + } else res.send({ success: true, message: "Route Template was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Route Template with id=" + id + }); + }); +}; + +// Delete a Route Template by id +exports.deleteRouteTemplate= (req, res) => { + const id = req.params.id; + RoutePathTemplate.findByIdAndRemove(id) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot delete Route Template with id=${id}. Maybe Route Template was not found!` + }); + } else { + res.send({ + message: "Route Template was deleted successfully!" + }); + } + }) + .catch(err => { + res.status(500).send({ + message: "Could not delete Route Template with id=" + id + }); + }); +}; diff --git a/app/controllers/route-path.controller.js b/app/controllers/route-path.controller.js new file mode 100644 index 0000000..f6c4f0d --- /dev/null +++ b/app/controllers/route-path.controller.js @@ -0,0 +1,249 @@ +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const RoutePath = db.route_path; +const CenterPhone = db.center_phone; +const Employee = db.employee; +exports.createRoutePath = (req, res) => { + // Validate request + if (!req.body.name) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + const site = splitSite.findSiteNumber(req); + // Create a Route + const routePath = new RoutePath({ + name: req.body.name, + schedule_date: req.body.schedule_date, + vehicle: req.body.vehicle, + status: req.body.status, + driver: req.body.driver, + type: req.body.type, + start_mileage: req.body.start_mileage, + end_mileage: req.body.end_mileage, + start_time: req.body.start_time || null, + end_time: req.body.end_time || null, + estimated_start_time: req.body.estimated_start_time || null, + route_customer_list: req.body.route_customer_list || [], + checklist_result: req.body.checklist_result || [], + site + }); + // Save Route in the database + routePath + .save(routePath) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Route." + }); + }); +}; +// Retrieve all Routes from the database. +exports.getAllRoutes = (req, res) => { + var params = req.query; + var condition = {}; + if (params) { + if (params.scheduleDate) { + condition.schedule_date = params.scheduleDate; + } + if (params.driverId) { + condition.driver = params.driverId; + } + } + condition.status = { "$ne": 'disabled' }; + condition = splitSite.splitSiteGet(req, condition); + RoutePath.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Routes." + }); + }); +}; + +// Retrieve all Routes with phones from the database. +exports.getAllRoutesWithPhones = (req, res) => { + var params = req.query; + var condition = {}; + var condition2 = {}; + if (params) { + if (params.scheduleDate) { + condition.schedule_date = params.scheduleDate; + } + if (params.driverId) { + condition.driver = params.driverId; + } + if (params.activated) { + condition2.activated = params.activated + } + } + condition.status = { "$ne": 'disabled' }; + condition = splitSite.splitSiteGet(req, condition); + condition2 = splitSite.splitSiteGet(req, condition2); + Promise.all( + [RoutePath.find(condition), CenterPhone.find(condition2)] + ).then(([transRoutes, phones]) => res.send({ + transRoutes: transRoutes, + centerPhones: phones + })).catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Routes or phones." + }); + }); +}; + +// Get One Route by Id +exports.getRoute = (req, res) => { + const id = req.params.id; + RoutePath.findById(id) + .then(data => { + if (!data) + res.status(404).send({ message: "Not found Route with id " + id }); + else { + // console.log('where is driver', data?.driver); + // Employee.findByIdAndUpdate(driver, {fetch_route_time: new Date()}).then((rst) => { + // console.log('updated result',rst); + // }).catch((err) => { + // console.log(`employee ${driver} update failure`) + // }) + res.send(data); + } + }) + .catch(err => { + res + .status(500) + .send({ message: "Error retrieving Route with id=" + id }); + }); +}; + +// Update a Route by the id in the request +exports.updateRoute = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + RoutePath.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update Route with id=${id}. Maybe Route was not found!` + }); + } else res.send({ success: true, message: "Route was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Route with id=" + id + }); + }); +}; + +// update route from mobile without deleting customers +exports.updateRouteInProgress = (req, res) => { + if (!req.body) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + const routeBody = req.body; + RoutePath.findById(routeBody.id) + .then(data => { + if (!data) + res.status(404).send({ message: "Not found Route with id " + id }); + else { + const currentRoute = data; + const currentCustomerList = currentRoute.route_customer_list; + const customerListSent = routeBody.route_customer_list; + const finalCustomerList = [...customerListSent]; + for (const cust of currentCustomerList) { + if (!customerListSent.find((c) => c.id === cust.id )) { + finalCustomerList.push(cust); + } + } + const finalBody = Object.assign({}, routeBody, {route_customer_list: finalCustomerList}) + RoutePath.findByIdAndUpdate(currentRoute.id, finalBody, { useFindAndModify: false }) + .then(data => { + // console.log('success', data.id); + // const driver = currentRoute?.driver; + // if (driver) { + // Employee.findById(driver).then((data) => { + // const employeeObj = data; + // // console.log('driverOjb', employeeObj) + // if (employeeObj) { + // const update_route_history = employeeObj?.update_route_history; + // const newHistoryItem = { + // route_id: currentRoute?.id, + // route_name: currentRoute?.name, + // route_schedule_date: currentRoute?.schedule_date, + // route_update_time: new Date() + // }; + // let result = []; + // if (update_route_history?.length >= 20) { + // for (let i=1; i < update_route_history.length; i++) { + // result.push(update_route_history[i]); + // } + // result.push(newHistoryItem); + // } else { + // update_route_history.push(newHistoryItem); + // result = update_route_history; + // } + // // console.log('final', result); + // Employee.findByIdAndUpdate(driver, Object.assign({}, employeeObj, {update_route_history: result})).then((rst) => { + // console.log('updated result',rst); + // }).catch((err) => { + // console.log(`employee ${driver} update failure`) + // }) + // } + // }) + // } + if (!data) { + res.status(404).send({ + message: `Cannot update Route with id=${id}. Maybe Route was not found!` + }); + } else res.send({ success: true, message: "Route was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Route with id=" + id + }); + }); + + // res.send({ success: true, message: "Route was found successfully." }); + } + }) + .catch(err => { + res + .status(500) + .send({ message: "Error retrieving Route with id=" + id }); + }); +} + +// Delete a Route by id +exports.deleteRoute= (req, res) => { + const id = req.params.id; + RoutePath.findByIdAndRemove(id) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot delete Route with id=${id}. Maybe Route was not found!` + }); + } else { + res.send({ + message: "Route was deleted successfully!" + }); + } + }) + .catch(err => { + res.status(500).send({ + message: "Could not delete Route with id=" + id + }); + }); +}; diff --git a/app/controllers/signature-request.controller.js b/app/controllers/signature-request.controller.js new file mode 100644 index 0000000..951de37 --- /dev/null +++ b/app/controllers/signature-request.controller.js @@ -0,0 +1,126 @@ +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const SignatureRequest = db.signature_request; + +// Create a new Signature Request +exports.createNewSignatureRequest = (req, res) => { + // Validate request + if (!req.body.route_id) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + const site = splitSite.findSiteNumber(req); + // Create an Signature Request + const signatureRequest = new SignatureRequest({ + driver_id: req.body.driver_id, + driver_name: req.body.driver_name, + route_id: req.body.route_id, + route_date: req.body.route_date, + route_name: req.body.route_name, + status: req.body.status || 'active', + create_by: req.body.create_by, + create_date: req.body.create_date, + site + }); + // Save Signature Request in the database + signatureRequest + .save(signatureRequest) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Signature Request Record." + }); + }); +}; + +// Retrive all SignatureRequest from database. +exports.getAllSignatureRequests = (req, res) => { + var params = req.query; + var condition = {}; + if (params.route_date) { + condition.route_date = params.route_date; + } + if (params.route_id) { + condition.route_id = params.route_id; + } + if (params.driver_id) { + condition.driver_id = params.driver_id; + } + + condition = splitSite.splitSiteGet(req, condition); + SignatureRequest.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Signature Requests." + }); + }); +}; + +// Get One Signature Request by Id +exports.getSignatureRequest = (req, res) => { + const id = req.params.id; + SignatureRequest.findById(id) + .then(data => { + if (!data) + res.status(404).send({ message: "Not found Signature Request with id " + id }); + else res.send(data); + }) + .catch(err => { + res + .status(500) + .send({ message: "Error retrieving Signature Request with id=" + id }); + }); +}; + +// Update a Signature Request by the id in the request +exports.updateSignatureRequest = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + SignatureRequest.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update Signature Request with id=${id}. Maybe Signature Request was not found!` + }); + } else res.send({ success: true, message: "Signature Request was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Signature Request with id=" + id + }); + }); +}; + +// Delete a Signature Request by id +exports.deleteSignatureRequest= (req, res) => { + const id = req.params.id; + SignatureRequest.findByIdAndRemove(id) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot delete Signature Request with id=${id}. Maybe Signature Request was not found!` + }); + } else { + res.send({ + message: "Signature Request was deleted successfully!" + }); + } + }) + .catch(err => { + res.status(500).send({ + message: "Could not delete Signature Request with id=" + id + }); + }); +}; \ No newline at end of file diff --git a/app/controllers/snack.controller.js b/app/controllers/snack.controller.js new file mode 100644 index 0000000..638aa74 --- /dev/null +++ b/app/controllers/snack.controller.js @@ -0,0 +1,119 @@ +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const Snack = db.snack; + +// Create a new Snack Item +exports.createNewSnack = (req, res) => { + // Validate request + if (!req.body.customer_id) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + const site = splitSite.findSiteNumber(req); + // Create an Snack Item + const snack = new Snack({ + customer_id: req.body.customer_id, + customer_name: req.body.customer_name, + has_snack: req.body.has_snack, + create_by: req.body.create_by, + create_date: req.body.create_date, + edit_history: req.body.edit_history, + date: req.body.date, + site + }); + // Save snack Item in the database + snack + .save(snack) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Snack Record." + }); + }); +}; + +// Retrive all Snack Records from database. +exports.getAllSnacks = (req, res) => { + var params = req.query; + var condition = {}; + if (params.date) { + condition.date = params.date; + } + + condition = splitSite.splitSiteGet(req, condition); + Snack.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Snacks." + }); + }); +}; + +// Get One Event by Id +// exports.getEvent = (req, res) => { +// const id = req.params.id; +// CalendarEvent.findById(id) +// .then(data => { +// if (!data) +// res.status(404).send({ message: "Not found Event with id " + id }); +// else res.send(data); +// }) +// .catch(err => { +// res +// .status(500) +// .send({ message: "Error retrieving Event with id=" + id }); +// }); +// }; + +// Update a Snack by the id in the request +exports.updateSnack = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + Snack.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update Snack with id=${id}. Maybe Snack was not found!` + }); + } else res.send({ success: true, message: "Snack was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Snack with id=" + id + }); + }); +}; + +// Delete a Snack by id +exports.deleteSnack= (req, res) => { + const id = req.params.id; + Snack.findByIdAndRemove(id) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot delete Snack with id=${id}. Maybe Snack was not found!` + }); + } else { + res.send({ + message: "Snack was deleted successfully!" + }); + } + }) + .catch(err => { + res.status(500).send({ + message: "Could not delete Snack with id=" + id + }); + }); +}; \ No newline at end of file diff --git a/app/controllers/staff.controller.js b/app/controllers/staff.controller.js new file mode 100644 index 0000000..2f138f4 --- /dev/null +++ b/app/controllers/staff.controller.js @@ -0,0 +1,114 @@ +const db = require("../models"); +const Staff = db.staff; + +var bcrypt = require("bcryptjs"); +const { splitSite } = require("../middlewares"); +// Create and Save a new Staff (driver, distributor, admin) +exports.createStaff = (req, res) => { + // Validate request + if (!req.body.username) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + const site = splitSite.findSiteNumber(req); + // Create a Staff + const staff = new Staff({ + username: req.body.username || req.body.email || '', + name: req.body.name || '', + name_cn: req.body.name_cn || '', + email: req.body.email || '', + password: req.body.password ? bcrypt.hashSync(req.body.password, 8) : '', + roles: req.body.roles || [], + mobile_phone: req.body.mobile_phone || '', + phone: req.body.phone || '', + home_phone: req.body.home_phone || '', + language: req.body.language || '', + employment_status: req.body.employment_status || '', + status: req.body.status || 'active', + title: req.body.title || '', + title_cn: req.body.title_cn || '', + firstname: req.body.firstname || '', + lastname: req.body.lastname || '', + department: req.body.department || '', + birth_date: req.body.birth_date || null, + driver_capacity: req.body.driver_capacity || null, + date_hired: req.body.date_hired || null, + create_by: req.body.create_by || '', + create_date: new Date(), + edit_by: req.body.edit_by || '', + edit_date: new Date(), + salt: req.body.salt || '264897', + city: req.body.city || '', + state: req.body.state || '', + zipcode: req.body.zipcode || '', + group: req.body.group || [], + tags: req.body.tags || [], + parent_id: req.body.parent_id || '5eee3552b02fac3d4acfd5ea', + site + }); + // Save Staff in the database + staff + .save(staff) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Staff." + }); + }); +}; + +// Update a Staff by the id in the request +exports.updateStaff = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + if (req.body.password) { + req.body.password = bcrypt.hashSync(req.body.password, 8); + } + Staff.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update staff with id=${id}. Maybe staff was not found!` + }); + } else res.send({ success: true, message: "Staff was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error updating Staff with id=" + id + }); + }); +}; +// Get Staff with username or email +exports.getStaffsWithNameOrEmail = (req, res) => { + var params = req.query; + var condition = {}; + const nameOrEmail = params?.nameOrEmail; + if (nameOrEmail) { + condition = { $or: [ + { email: nameOrEmail }, + { name: nameOrEmail } + ]}; + } + condition = splitSite.splitSiteGet(req, condition); + Staff.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving staffs." + }); + }); +}; + + + diff --git a/app/controllers/timedata.controller.js b/app/controllers/timedata.controller.js new file mode 100644 index 0000000..bf7c827 --- /dev/null +++ b/app/controllers/timedata.controller.js @@ -0,0 +1,25 @@ +const axios = require("axios"); +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const Timedata = db.timedata; +var moment = require('moment-timezone'); + +exports.getDocsByCondition1 = (req, res) => { + const params = req.query; + const date = params.date; + const condition = {}; + condition['time'] = { + //$gte: moment(parameter.date + 'T00:00:00.00').toDate() + $gte: moment.tz(date + 'T00:00:00.00', "America/New_York").toDate() + }; + Timedata.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving time data." + }); + }); +} \ No newline at end of file diff --git a/app/controllers/upload.controller.js b/app/controllers/upload.controller.js new file mode 100644 index 0000000..8acb0cc --- /dev/null +++ b/app/controllers/upload.controller.js @@ -0,0 +1,70 @@ +const upload = require("../middlewares/upload"); +const dbConfig = require("../config/db.config"); +const MongoClient = require("mongodb").MongoClient; +const GridFSBucket = require("mongodb").GridFSBucket; +const url = dbConfig.url; +const baseUrl = dbConfig.fileUrl; +const mongoClient = new MongoClient(url); +const uploadFiles = async (req, res) => { + try { + await upload(req, res); + if (req.file == undefined) { + return res.send({ + message: "You must select a file.", + }); + } + return res.send({ + message: "File has been uploaded.", + }); + } catch (error) { + console.log(error); + return res.send({ + message: "Error when trying upload image: ${error}", + }); + } +}; +const getFile = async (req, res) => { + try { + await mongoClient.connect(); + const database = mongoClient.db(dbConfig.database); + const images = database.collection(dbConfig.imgBucket + ".files"); + const chunks = database.collection(dbConfig.imgBucket + ".chunks"); + const cursor = await (images.find({filename: req.params.name}).toArray()); + if (cursor.length === 0) { + return res.status(500).send({ + message: "No files found!", + }); + } + const chunkCursor = await(chunks.find({files_id: cursor[cursor.length-1]._id}).toArray()); + return res.status(200).send(chunkCursor[0].data); + } catch (error) { + return res.status(500).send({ + message: error.message, + }); + } +}; + +const deleteFile = async (req, res) => { + try { + await mongoClient.connect(); + const database = mongoClient.db(dbConfig.database); + const images = database.collection(dbConfig.imgBucket + ".files"); + const chunks = database.collection(dbConfig.imgBucket + ".chunks"); + const cursor = await (images.find({filename: req.body.name}).toArray()); + if (cursor.length > 0) { + await chunks.deleteMany({files_id: cursor[cursor.length-1]._id}); + await images.deleteMany({filename: req.body.name}); + } + return res.status(200).send({ message: 'Delete Image Succeed'}); + } catch (error) { + return res.status(500).send({ + message: error.message, + }); + } +}; + +module.exports = { + uploadFiles, + getFile, + deleteFile +}; \ No newline at end of file diff --git a/app/controllers/user.controller.js b/app/controllers/user.controller.js new file mode 100644 index 0000000..5fbf5a7 --- /dev/null +++ b/app/controllers/user.controller.js @@ -0,0 +1,30 @@ +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const User = db.user; +// Create and Save a new User +exports.createUser = (req, res) => { + +}; +// Retrieve all Users from the database. +exports.getAllUsers = (req, res) => { + var condition = {}; + condition = splitSite.splitSiteGet(req, condition); + User.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving users." + }); + }); +}; +// Update a User by the id in the request +exports.updateUser = (req, res) => { + +}; +// Delete a User with the specified id in the request +exports.delete = (req, res) => { + +}; diff --git a/app/controllers/vehicle.controller.js b/app/controllers/vehicle.controller.js new file mode 100644 index 0000000..864e019 --- /dev/null +++ b/app/controllers/vehicle.controller.js @@ -0,0 +1,133 @@ +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const Vehicle = db.vehicle; +exports.createVehicle = (req, res) => { + // Validate request + if (!req.body.tag) { + res.status(400).send({ message: "Content can not be empty!" }); + return; + } + const site = splitSite.findSiteNumber(req); + // Create a Vehicle + const vehicle = new Vehicle({ + vehicle_number: req.body.vehicle_number, + tag: req.body.tag || '', + ezpass: req.body.ezpass || '', + gps_tag: req.body.gps_tag || '', + mileage: req.body.mileage || 0, + capacity: req.body.capacity || 0, + make: req.body.make || '', + vehicle_model: req.body.vehicle_model || '', + year: req.body.year || '', + checklist: req.body.checklist || '', + status: 'active', + site + }); + + // Save Vehicle in the database + vehicle + .save(vehicle) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while creating the Vehicle." + }); + }); +}; +// Retrieve all Vehicles from the database. +exports.getAllVehicles = (req, res) => { + var condition = {}; + condition = splitSite.splitSiteGet(req, condition); + Vehicle.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Vehicles." + }); + }); +}; +// Retrieve all Active Vehicles from the database. +exports.getAllActiveVehicles = (req, res) => { + var condition = { status: 'active' }; + condition = splitSite.splitSiteGet(req, condition); + Vehicle.find(condition) + .then(data => { + res.send(data); + }) + .catch(err => { + res.status(500).send({ + message: + err.message || "Some error occurred while retrieving Vehicles." + }); + }); +}; +// Get One Vehicle by Id +exports.getVehicle = (req, res) => { + const id = req.params.id; + Vehicle.findById(id) + .then(data => { + if (!data) + res.status(404).send({ message: "Not found Vehicle with id " + id }); + else res.send(data); + }) + .catch(err => { + res + .status(500) + .send({ message: "Error retrieving Vehicle with id=" + id }); + }); +}; +// Update a Vehicle by the id in the request +exports.updateVehicle = (req, res) => { + if (!req.body) { + return res.status(400).send({ + message: "Data to update can not be empty!" + }); + } + const id = req.params.id; + Vehicle.findByIdAndUpdate(id, req.body, { useFindAndModify: false }) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot update Vehicle with id=${id}. Maybe Vehicle was not found!` + }); + } else res.send({ success: true, message: "Vehicle was updated successfully." }); + }) + .catch(err => { + res.status(500).send({ + success: false, + message: "Error" + (err.message || "") + "updating Vehicle with id=" + id + }); + }); +}; +// Soft Delete a Vehicle +exports.deactivateVehicle = (req, res) => { + +}; + +// Delete a Vehicle by id +exports.deleteVehicle= (req, res) => { + const id = req.params.id; + Vehicle.findByIdAndRemove(id) + .then(data => { + if (!data) { + res.status(404).send({ + message: `Cannot delete Vehicle with id=${id}. Maybe Vehicle was not found!` + }); + } else { + res.send({ + message: "Vehicle was deleted successfully!" + }); + } + }) + .catch(err => { + res.status(500).send({ + message: "Could not delete Vehicle with id=" + id + ": " + err.message || "" + }); + }); +}; diff --git a/app/controllers/xlsxtemplate.controller.js b/app/controllers/xlsxtemplate.controller.js new file mode 100644 index 0000000..1386898 --- /dev/null +++ b/app/controllers/xlsxtemplate.controller.js @@ -0,0 +1,413 @@ +// const axios = require("axios"); +// const { splitSite } = require("../middlewares"); +// const db = require("../models"); +// const ExcelTemplate = db.exceltemplate; +// var XlsxTemplate = require('xlsx-template'); +// var fs = require('fs'); +// var multer = require('multer'); +// const path = require('path'); +// var libre = require('libreoffice-convert'); + +// exports.createPDFFromSheetTemplateName = (req, res) => { +// console.log('what', __dirname.replace('/controllers', '')); +// var inputData = req.query.inputData; +// var excelTemplateName = req.query.excelTemplateName; +// // var transportationId = req.body.transportationId;d +// // var transportationName = req.body.transportationName; +// ExcelTemplate.find({name: excelTemplateName}).then((data) => { +// try { +// if (data && data.length > 0) { +// var excelTemplate = data[0] || {}; +// var templateExcel = `${__dirname.replace('/controllers', '')}${excelTemplate?.file[0]?.url}`; +// var outputFileRandom = Math.ceil(Math.random() * 100000000); +// var outputFile = `/tmp/${excelTemplate.name}_${outputFileRandom}.xlsx`; +// var data = fs.readFileSync(templateExcel, 'binary'); +// var template = new XlsxTemplate(data); +// var events = JSON.parse(inputData)?.events; +// for (var i = 0; i < events.length; i++) { +// var pageNumber = i + 2; +// var event = events[i]; +// // template.copySheet(1, pageNumber); +// template.copySheet(1, event.client_name); +// // console.log('>>> template substitute:', pageNumber, event); +// template.substitute(pageNumber, event); +// } +// template.deleteSheet(1); +// var newData = template.generate(); +// fs.writeFileSync(outputFile, newData, 'binary'); +// var outputFilename = outputFile || {}; +// var outputPdfPath = outputFilename.substr(0, outputFilename.length - 5) + '.pdf'; +// console.log('outputPDF:', outputPdfPath); +// var extend = 'pdf'; + +// var infile = fs.readFileSync(outputFile); +// // Convert it to pdf format with undefined filter (see Libreoffice doc about filter) +// try { +// libre.convert(infile, extend, undefined, (err, done) => { +// if (err) { +// console.log('Error converting file:', err); +// } +// // Here in done you have pdf file which you can save or transfer in another stream +// fs.writeFileSync(outputPdfPath, done); +// console.log('Conver xlsx to pdf, Done.'); +// res.download(outputPdfPath, function(error) { +// if (error) { +// console.log('Error in sending download file ${outputPdfPath}'); +// } +// }); +// }); +// } catch (e) { +// console.log(e); +// } + +// } else { +// res.status(404).send({ +// success: false, +// message: "Sheet Template doesn't exist" +// }) +// } +// } catch(e) { +// console.log(e); +// } +// }).catch(err => { +// res.status(500).send({ +// success: false, +// message: "Error Geting docs" +// }); +// }); +// } + +// exports.createSheetFromTemplateName = (req, res) => { +// console.log('what', __dirname.replace('/controllers', '')); +// var inputData = req.query.inputData; +// console.log('inputData', inputData); +// var excelTemplateName = req.query.excelTemplateName; +// // var transportationId = req.body.transportationId; +// // var transportationName = req.body.transportationName; +// ExcelTemplate.find({name: excelTemplateName}).then((data) => { +// try { +// if (data && data.length > 0) { +// var excelTemplate = data[0] || {}; +// console.log('template', excelTemplate); +// var templateExcel = `${__dirname.replace('/controllers', '')}${excelTemplate?.file[0]?.url}`; +// var outputFileRandom = Math.ceil(Math.random() * 100000000); +// var outputFile = `/tmp/${excelTemplate.name}_${outputFileRandom}.xlsx`; +// var data = fs.readFileSync(templateExcel, 'binary'); +// var template = new XlsxTemplate(data); +// var events = JSON.parse(inputData)?.events; +// console.log('events',events) +// for (var i = 0; i < events.length; i++) { +// var pageNumber = i + 2; +// var event = events[i]; +// // template.copySheet(1, pageNumber); +// template.copySheet(1, event.client_name); +// // console.log('>>> template substitute:', pageNumber, event); +// template.substitute(pageNumber, event); +// } +// template.deleteSheet(1); +// var newData = template.generate(); +// fs.writeFileSync(outputFile, newData, 'binary'); +// console.log('test'); +// res.download(outputFile, (error) => { +// if (error) { +// console.log('Error in downloading excel') +// } +// }) +// console.log('Download finish'); +// } else { +// res.status(404).send({ +// success: false, +// message: "excel Template doesn't exist" +// }) +// } +// } catch(e) { +// console.log(e); +// } +// }).catch(err => { +// res.status(500).send({ +// success: false, +// message: "Error Geting excels" +// }); +// }); +// // ids.forEach(id => { +// // CalendarEvent.findByIdAndUpdate(id, {link_event_uuid: transportationId, +// // link_event_name: transportationName}, { useFindAndModify: false }) +// // .then(data => { +// // if (!data) { +// // res.status(404).send({ +// // message: `Cannot update Event with id=${id}. Maybe Event was not found!` +// // }); +// // } else res.send({ success: true, message: "Event was updated successfully." }); +// // }) +// // .catch(err => { +// // res.status(500).send({ +// // success: false, +// // message: "Error updating Event with id=" + id +// // }); +// // }); +// // }) +// } + + + +const axios = require("axios"); +const { splitSite } = require("../middlewares"); +const db = require("../models"); +const ExcelTemplate = db.exceltemplate; +const Event = db.calendar_event; +const Customer = db.customer; +const Client = db.client; +const Resource = db.resource; +var XlsxTemplate = require('xlsx-template'); +var fs = require('fs'); +var multer = require('multer'); +const path = require('path'); +var libre = require('libreoffice-convert'); +const moment = require('moment-timezone'); + +exports.createPDFFromSheetTemplateName = (req, res) => { + console.log('what', __dirname.replace('/controllers', '')); + var inputData = JSON.parse(req.query.inputData); + const eventIds = inputData?.eventIds; + console.log('eventIds',eventIds); + var excelTemplateName = req.query.excelTemplateName; + console.log('template', excelTemplateName); + + Event.find({ '_id': { $in: eventIds } }).then((events) => { + + Resource.find({}).then((resources) => { + Customer.find({}).then((customers) => { + Client.find({}).then((clients) => { + const docData = {events: []}; + for (const event of events) { + const customerModels = event?.data?.customer ? customers: clients; + const customerId = event?.data?.customer ? event?.data?.customer : event?.target_uuid; + const customer = customerModels.find((customerModel) => customerModel?.id === customerId || customerModel?._id === customerId || customerModel?.name === event?.data?.client_name ); + let pharmacyInfo = 'This patient has no contracted pharmacy.\n' + 'Please give the prescription directly to the patient'; + const pharmacy = resources.find(r => (r.id === customer?.pharmacy_id)) || resources.find(r => (r.name?.toLowerCase() === event?.data?.client_pharmacy?.toLowerCase())); + + if (pharmacy) { + pharmacyInfo = `${pharmacy?.name} @ ${pharmacy?.address} \n` + + `phone:${pharmacy?.phone} fax:${pharmacy?.fax}`; + } else { + if (event?.data?.client_pharmacy) { + pharmacyInfo = 'Pharmacy: ' + event?.data?.client_pharmacy; + } + } + + const nameShow = event?.data?.customer ? `${event?.data?.client_name} ${customer?.name_cn}` : `${customer?.name_cn}` + + const clientName = (!!customer?.name_cn && !!event?.data?.client_name && nameShow) || customer?.name_cn || (customer?.lastname && customer?.firstname && `${customer?.lastname}, ${customer?.firstname}`) || event?.data?.client_name; + + docData.events.push({ + event_time: moment(event?.start_time).format('MM/DD/YYYY hh:mm A'), + client_name: event?.data?.client_name, // event.data.client_name, + client_status: '会员', + client_dob: event.data.client_birth_date, + // client_pcp: event.data.client_pcp, + // client_pharmacy: event.data.client_pharmacy, + client_seating: event.data.client_seating, + doctor_name: event.data.resource_name, + doctor_phone: event.data.resource_phone, + //doctor_contact: event.data.resource_contact, + doctor_address: event.data.resource_address?.replaceAll('\n', ' '), + //title: event.title, + // description: event.description, + interpreter: event.data.interpreter, + fasting: event.data.fasting, + confirmed: event.data.confirmed, + new_patient: event.data.new_patient, + doc_order: event.data.doc_order, + need_id: event.data.need_id, + need_med_list: event.data.need_med_list, + reason: event.data.reason, + // notes: event.data.notes, + other: event.data.other, + pharmacy_info: pharmacyInfo, + client_preferred_name: customer?.name_cn, + member_type: customer?.type, + disability: (customer?.disability === true) ? 'Yes' : (event?.data?.disability || 'No') + }); + + } + + // var transportationId = req.body.transportationId;d + // var transportationName = req.body.transportationName; + ExcelTemplate.find({name: excelTemplateName}).then((data) => { + try { + if (data && data.length > 0) { + var excelTemplate = data[0] || {}; + var templateExcel = `${__dirname.replace('/controllers', '')}${excelTemplate?.file[0]?.url}`; + var outputFileRandom = Math.ceil(Math.random() * 100000000); + var outputFile = `/tmp/${excelTemplate.name}_${outputFileRandom}.xlsx`; + var data = fs.readFileSync(templateExcel, 'binary'); + var template = new XlsxTemplate(data); + var eventDatas = docData.events; + for (var i = 0; i < eventDatas.length; i++) { + var pageNumber = i + 2; + var eventData = eventDatas[i]; + // template.copySheet(1, pageNumber); + template.copySheet(1, eventData.client_name); + // console.log('>>> template substitute:', pageNumber, event); + template.substitute(pageNumber, eventData); + } + template.deleteSheet(1); + var newData = template.generate(); + fs.writeFileSync(outputFile, newData, 'binary'); + var outputFilename = outputFile || {}; + var outputPdfPath = outputFilename.substr(0, outputFilename.length - 5) + '.pdf'; + console.log('outputPDF:', outputPdfPath); + var extend = 'pdf'; + + var infile = fs.readFileSync(outputFile); + // Convert it to pdf format with undefined filter (see Libreoffice doc about filter) + try { + libre.convert(infile, extend, undefined, (err, done) => { + if (err) { + console.log('Error converting file:', err); + } + // Here in done you have pdf file which you can save or transfer in another stream + fs.writeFileSync(outputPdfPath, done); + console.log('Conver xlsx to pdf, Done.'); + res.download(outputPdfPath, function(error) { + if (error) { + console.log('Error in sending download file ${outputPdfPath}'); + } + }); + }); + } catch (e) { + console.log(e); + } + + } else { + res.status(404).send({ + success: false, + message: "Sheet Template doesn't exist" + }) + } + } catch(e) { + console.log(e); + } + }).catch(err => { + res.status(500).send({ + success: false, + message: "Error Geting docs" + }); + }); + + }) + }) + }); + }) +} + +exports.createSheetFromTemplateName = (req, res) => { + console.log('what', __dirname.replace('/controllers', '')); + var inputData = JSON.parse(req.query.inputData); + const eventIds = inputData?.eventIds; + var excelTemplateName = req.query.excelTemplateName; + + Event.find({ '_id': { $in: eventIds } }).then((events) => { + Resource.find({}).then((resources) => { + Customer.find({}).then((customers) => { + Client.find({}).then((clients) => { + const docData = {events: []}; + for (const event of events) { + const customerModels = event?.data?.customer ? customers: clients; + const customerId = event?.data?.customer ? event?.data?.customer : event?.target_uuid; + const customer = customerModels.find((customerModel) => customerModel?.id === customerId || customerModel?._id === customerId || customerModel?.name === event?.data?.client_name ); + let pharmacyInfo = 'This patient has no contracted pharmacy.\n' + 'Please give the prescription directly to the patient'; + const pharmacy = resources.find(r => (r.id === customer?.pharmacy_id)) || resources.find(r => (r.name?.toLowerCase() === event?.data?.client_pharmacy?.toLowerCase())); + + if (pharmacy) { + pharmacyInfo = `${pharmacy?.name} @ ${pharmacy?.address} \n` + + `phone:${pharmacy?.phone} fax:${pharmacy?.fax}`; + } else { + if (event?.data?.client_pharmacy) { + pharmacyInfo = 'Pharmacy: ' + event?.data?.client_pharmacy; + } + } + + const clientName = (customer?.name_cn && event?.data?.client_name && `${event?.data?.client_name} ${customer?.name_cn}`) || customer?.name_cn || (customer?.lastname && customer?.firstname && `${customer?.lastname}, ${customer?.firstname}`) || event?.data?.client_name; + + docData.events.push({ + event_time: moment(event?.start_time).format('MM/DD/YYYY hh:mm A'), + client_name: event?.data?.client_name, // event.data.client_name, + client_status: '会员', + client_dob: event.data.client_birth_date, + // client_pcp: event.data.client_pcp, + // client_pharmacy: event.data.client_pharmacy, + client_seating: event.data.client_seating, + doctor_name: event.data.resource_name, + doctor_phone: event.data.resource_phone, + //doctor_contact: event.data.resource_contact, + doctor_address: event.data.resource_address?.replaceAll('\n', ' '), + //title: event.title, + // description: event.description, + interpreter: event.data.interpreter, + fasting: event.data.fasting, + confirmed: event.data.confirmed, + new_patient: event.data.new_patient, + doc_order: event.data.doc_order, + need_id: event.data.need_id, + need_med_list: event.data.need_med_list, + reason: event.data.reason, + // notes: event.data.notes, + other: event.data.other, + pharmacy_info: pharmacyInfo, + client_preferred_name: customer?.name_cn, + member_type: customer?.type, + disability: (customer?.disability === true) ? 'Yes' : (event?.data?.disability || 'No') + }); + } + // var transportationId = req.body.transportationId;d + // var transportationName = req.body.transportationName; + ExcelTemplate.find({name: excelTemplateName}).then((data) => { + try { + if (data && data.length > 0) { + var excelTemplate = data[0] || {}; + var templateExcel = `${__dirname.replace('/controllers', '')}${excelTemplate?.file[0]?.url}`; + var outputFileRandom = Math.ceil(Math.random() * 100000000); + var outputFile = `/tmp/${excelTemplate.name}_${outputFileRandom}.xlsx`; + var data = fs.readFileSync(templateExcel, 'binary'); + var template = new XlsxTemplate(data); + var eventDatas = docData.events; + for (var i = 0; i < eventDatas.length; i++) { + var pageNumber = i + 2; + var eventData = eventDatas[i]; + // template.copySheet(1, pageNumber); + template.copySheet(1, eventData.client_name); + // console.log('>>> template substitute:', pageNumber, event); + template.substitute(pageNumber, eventData); + } + template.deleteSheet(1); + var newData = template.generate(); + fs.writeFileSync(outputFile, newData, 'binary'); + console.log('test'); + res.download(outputFile, (error) => { + if (error) { + console.log('Error in downloading excel') + } + }) + console.log('Download finish'); + } else { + res.status(404).send({ + success: false, + message: "Sheet Template doesn't exist" + }) + } + } catch(e) { + console.log(e); + } + }).catch(err => { + res.status(500).send({ + success: false, + message: "Error Geting docs" + }); + }); + + }) + }) + }); + }) +} diff --git a/app/middlewares/.DS_Store b/app/middlewares/.DS_Store new file mode 100644 index 0000000..6164319 Binary files /dev/null and b/app/middlewares/.DS_Store differ diff --git a/app/middlewares/authJwt.js b/app/middlewares/authJwt.js new file mode 100644 index 0000000..e6fb90a --- /dev/null +++ b/app/middlewares/authJwt.js @@ -0,0 +1,23 @@ +const jwt = require("jsonwebtoken"); +const config = require("../config/auth.config.js"); +const db = require("../models"); +const User = db.user; +const Role = db.role; +verifyToken = (req, res, next) => { + let token = req.headers["x-access-token"]; + if (!token) { + return res.status(403).send({ message: "No token provided!" }); + } + jwt.verify(token, config.secret, (err, decoded) => { + if (err) { + return res.status(401).send({ message: "Unauthorized!" }); + } + req.userId = decoded.id; + next(); + }); +}; + +const authJwt = { + verifyToken +}; +module.exports = authJwt; \ No newline at end of file diff --git a/app/middlewares/index.js b/app/middlewares/index.js new file mode 100644 index 0000000..05728e4 --- /dev/null +++ b/app/middlewares/index.js @@ -0,0 +1,6 @@ +const authJwt = require("./authJwt"); +const splitSite = require("./splitSite"); +module.exports = { + authJwt, + splitSite +}; \ No newline at end of file diff --git a/app/middlewares/splitSite.js b/app/middlewares/splitSite.js new file mode 100644 index 0000000..3580054 --- /dev/null +++ b/app/middlewares/splitSite.js @@ -0,0 +1,45 @@ +const siteMap = { + 'ws1': 1, + 'worldshine1': 1, + 'ws2': 2, + 'worldshine2': 2, + 'ws3': 3, + 'worldshine3': 3, + 'worldshine4': 4, + 'ws4': 4, + 'worldshine.mayo.llc': 1 +}; + +const findSiteNumber = (req) => { + const hostname = req.hostname; + let site = 1; + for (const key of Object.keys(siteMap)) { + if (hostname.includes(key)) { + site = siteMap[key]; + break; + } + } + return site; +} + +const splitSiteGet = (req, queryParams) => { + const site = findSiteNumber(req); + const newQueryParams = { ...queryParams, site: site }; + console.log('query', newQueryParams); + return newQueryParams; +} + +const splitSitePost = (req, postBody) => { + const site = findSiteNumber(req); + const newPostBody = { ...postBody, site: site }; + console.log('post', newPostBody); + return newPostBody; +} + +const splitSite = { + findSiteNumber, + splitSiteGet, + splitSitePost +} + +module.exports = splitSite; \ No newline at end of file diff --git a/app/middlewares/upload.js b/app/middlewares/upload.js new file mode 100644 index 0000000..dd0d698 --- /dev/null +++ b/app/middlewares/upload.js @@ -0,0 +1,22 @@ +const util = require("util"); +const multer = require("multer"); +const { GridFsStorage } = require("multer-gridfs-storage"); +const dbConfig = require("../config/db.config"); +var storage = new GridFsStorage({ + url: dbConfig.url, + options: { useNewUrlParser: true, useUnifiedTopology: true }, + file: (req, file) => { + const match = ["image/png", "image/jpeg", "image/jpg"]; + if (match.indexOf(file.mimetype) === -1) { + const filename = req.params.filename; + return filename; + } + return { + bucketName: dbConfig.imgBucket, + filename: req.params.filename + }; + } +}); +var uploadFiles = multer({ storage: storage }).single("file"); +var uploadFilesMiddleware = util.promisify(uploadFiles); +module.exports = uploadFilesMiddleware; \ No newline at end of file diff --git a/app/models/breakfast.model.js b/app/models/breakfast.model.js new file mode 100644 index 0000000..c302bac --- /dev/null +++ b/app/models/breakfast.model.js @@ -0,0 +1,29 @@ +module.exports = mongoose => { + var editHistorySchema = mongoose.Schema({ + employee: String, + date: Date + }); + + var schema = mongoose.Schema( + { + customer_id: String, + customer_name: String, + date: String, + has_breakfast: Boolean, + create_by: String, + create_date: Date, + edit_history: [{ + type: editHistorySchema + }], + site: Number + }, + { collection: 'breakfast', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + const Breakfast = mongoose.model("breakfast", schema); + return Breakfast; + }; \ No newline at end of file diff --git a/app/models/calendar-event.model.js b/app/models/calendar-event.model.js new file mode 100644 index 0000000..dd7f142 --- /dev/null +++ b/app/models/calendar-event.model.js @@ -0,0 +1,91 @@ +module.exports = mongoose => { + var editHistorySchema = mongoose.Schema({ + employee: String, + date: Date + }); + var eventDataSchema = mongoose.Schema({ + customer: {type: mongoose.Schema.Types.ObjectId, ref: 'Customer'}, + resource: {type: mongoose.Schema.Types.ObjectId, ref: 'Resource'}, + // These fields are legacy fields, we will keep it but won't use it again + client_name: String, + client_pharmacy: String, + client_pcp: String, + client_birth_date: String, + client_seating: String, + resource_type: String, + resource_name: String, + resource_phone: String, + resource_contact: String, + resource_address: String, + resource_city: String, + resource_state: String, + // legacy fields end + // We still wanna keep the legacy fields below + new_patient: String, + confirmed: String, + fasting: String, + interpreter: String, + doc_order: String, + need_id: String, + need_med_list: String, + notes: String, + reason: String, + other: String, + disability: String, + disability_support: String, + video_type: String, + video_id: String, + presenter: String, + trans_method: String, + }); + var schema = mongoose.Schema( + { + title: String, + type: String, + // value could be ['shopping', 'banking', 'singing', 'dancing', 'medical', 'haircut', 'meal', 'transportation', 'others'] + descrption: String, + department: String, + notes: String, + start_time: Date, + stop_time: Date, + color: String, + source_type: String, + source_uuid: String, + source_name: String, + target_type: String, + target_uuid: String, + target_name: String, + link_event_uuid: String, + link_event_name: String, + data: eventDataSchema, + files: [{ + type: String + }], + status: String, + confirmed: Boolean, + // value could be ['active', 'inactive'] + signup_start_date: Date, + member_col: Object, + tags: [{ + type: String + }], + create_by: String, + create_date: Date, + edit_by: String, + edit_date: Date, + youtube_video_id: String, + edit_history: [{ + type: editHistorySchema + }], + site: Number + }, + { collection: 'calendar_event', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + const CalendarEvent = mongoose.model("calendar_event", schema); + return CalendarEvent; + }; \ No newline at end of file diff --git a/app/models/center-phone.model.js b/app/models/center-phone.model.js new file mode 100644 index 0000000..3d360b2 --- /dev/null +++ b/app/models/center-phone.model.js @@ -0,0 +1,20 @@ +const uniqueValidator = require('mongoose-unique-validator'); +module.exports = mongoose => { + var schema = mongoose.Schema( + { + phone_title: String, + phone_number: String, + activated: Boolean, + site: Number + }, + { collection: 'center_phone', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + schema.plugin(uniqueValidator); + const CenterPhone = mongoose.model("center_phone", schema); + return CenterPhone; + }; \ No newline at end of file diff --git a/app/models/client.model.js b/app/models/client.model.js new file mode 100644 index 0000000..d337498 --- /dev/null +++ b/app/models/client.model.js @@ -0,0 +1,74 @@ +const uniqueValidator = require('mongoose-unique-validator'); +module.exports = mongoose => { + var schema = mongoose.Schema( + { + username: { + type: String, + unique: true + }, + name: String, + name_cn: String, + email: { + type: String, + unique: true + }, + parent_id: String, + password: String, + care_provider: String, + emergency_contact: String, + medicare_number: String, + medicaid_number: String, + pharmacy: String, + birth_date: String, + firstname: String, + lastname: String, + address: String, + address2: String, + phone: String, + mobile_phone: String, + type: String, + avatar: String, + note: String, + language: String, + status: String, + create_by: String, + create_date: Date, + edit_by: String, + edit_date: Date, + password: String, + pharmacy_id: String, + pin: String, + admission_date: String, + home_phone: String, + seating: String, + vehicle_no: String, + caller: String, + roles: [{ + type: String + }], + discharge_date: String, + placement: String, + nickname: String, + salt: String, + groups: [{ + type: String + }], + tags: [{ + type: String + }], + api_token: String, + data: String, + title: String, + private_note: String, + site: Number + }, + { collection: 'client', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + const Client = mongoose.model("client", schema); + return Client; + }; \ No newline at end of file diff --git a/app/models/customer.model.js b/app/models/customer.model.js new file mode 100644 index 0000000..caffbc4 --- /dev/null +++ b/app/models/customer.model.js @@ -0,0 +1,86 @@ +const uniqueValidator = require('mongoose-unique-validator'); +module.exports = mongoose => { + var schema = mongoose.Schema( + { + username: { + type: String, + unique: true + }, + name: String, + name_cn: String, + email: { + type: String, + unique: true + }, + parent_id: String, + password: String, + care_provider: String, + emergency_contact: String, + medicare_number: String, + medicaid_number: String, + pharmacy: String, + birth_date: String, + firstname: String, + lastname: String, + address1: String, + address2: String, + address3: String, + address4: String, + address5: String, + phone: String, + mobile_phone: String, + type: String, + avatar: String, + special_needs: String, + note: String, + language: String, + status: String, + pickup_status: String, + create_by: String, + create_date: Date, + edit_by: String, + edit_date: Date, + password: String, + pharmacy_id: String, + pin: String, + admission_date: String, + home_phone: String, + seating: String, + vehicle_no: String, + caller: String, + roles: [{ + type: String + }], + discharge_date: String, + placement: String, + nickname: String, + table_id: String, + salt: String, + groups: [{ + type: String + }], + tags: [{ + type: String + }], + api_token: String, + data: String, + title: String, + apartment: String, + private_note: String, + site: Number, + disability: Boolean, + height: String, + weight: String, + gender: String, + text_msg_enabled: Boolean + }, + { collection: 'customer', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + const Customer = mongoose.model("customer", schema); + return Customer; + }; \ No newline at end of file diff --git a/app/models/doctemplate.model.js b/app/models/doctemplate.model.js new file mode 100644 index 0000000..b1f8b7a --- /dev/null +++ b/app/models/doctemplate.model.js @@ -0,0 +1,37 @@ +module.exports = mongoose => { + var editHistorySchema = mongoose.Schema({ + employee: String, + date: Date + }); + var schema = mongoose.Schema( + { + name: { + type: String, + required: true, + }, + content: String, + description: String, + model: Object, + status: String, // 'active', 'inactive' + file: [{ + type: Object + }], + site: Number, + create_by: String, + create_date: Date, + edit_by: String, + edit_date: Date, + edit_history: [{ + type: editHistorySchema + }], + }, + { collection: 'doctemplate', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + const DocTemplate = mongoose.model("doctemplate", schema); + return DocTemplate; + }; \ No newline at end of file diff --git a/app/models/employee.model.js b/app/models/employee.model.js new file mode 100644 index 0000000..398679e --- /dev/null +++ b/app/models/employee.model.js @@ -0,0 +1,49 @@ +const uniqueValidator = require('mongoose-unique-validator'); +module.exports = mongoose => { + var schema = mongoose.Schema( + { + username: String, + name: String, + name_cn: String, + email: String, + password: String, + roles: [{ + type: String + }], + mobile_phone: String, + phone: String, + home_phone: String, + language: String, + employment_status: String, + status: String, + address: String, + title: String, + title_cn: String, + firstname: String, + lastname: String, + department: String, + birth_date: String, + driver_capacity: Number, + date_hired: String, + create_by: String, + create_date: Date, + edit_by: String, + edit_date: Date, + note: String, + tags: [{ + type: String + }], + fetch_route_time: Date, + site: Number + }, + { collection: 'employee', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + schema.index({username: 1, email: 1, site:1}, {unique: true}); + const Employee = mongoose.model("employee", schema); + return Employee; + }; \ No newline at end of file diff --git a/app/models/event-request.model.js b/app/models/event-request.model.js new file mode 100644 index 0000000..6ec5600 --- /dev/null +++ b/app/models/event-request.model.js @@ -0,0 +1,39 @@ +module.exports = mongoose => { + var editHistorySchema = mongoose.Schema({ + employee: String, + date: Date + }); + var schema = mongoose.Schema( + { + customer_id: String, + customer_display: String, + source: String, + type: String, + symptom: String, + resource: String, + resource_display: String, + transportation: String, + np: String, + upload: String, + notes: [{ + content: String, + author: String + }], + status: String, + create_by: String, + create_date: Date, + edit_history: [{ + type: editHistorySchema + }], + site: Number + }, + { collection: 'event_request', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + const EventRequest = mongoose.model("event_request", schema); + return EventRequest; + }; \ No newline at end of file diff --git a/app/models/index.js b/app/models/index.js new file mode 100644 index 0000000..b1d55e4 --- /dev/null +++ b/app/models/index.js @@ -0,0 +1,30 @@ +const dbConfig = require("../config/db.config.js"); +const mongoose = require("mongoose"); +mongoose.Promise = global.Promise; +const db = {}; +db.mongoose = mongoose; +db.url = dbConfig.url; +db.user = require("./user.model.js")(mongoose); +db.employee = require("./employee.model.js")(mongoose); +db.staff = require("./staff.model")(mongoose); +db.vehicle = require("./vehicle.model")(mongoose); +db.customer = require("./customer.model")(mongoose); +db.route_path = require("./route-path.model")(mongoose); +db.route_path_template = require("./route-path-template.model")(mongoose); +db.report = require("./report.model")(mongoose); +db.client = require("./client.model")(mongoose); +db.message = require("./message.model")(mongoose); +db.resource = require("./resource.model")(mongoose); +db.center_phone = require("./center-phone.model")(mongoose); +db.message_token = require("./message-token.model")(mongoose); +db.sent_message = require("./sent-message.model")(mongoose); +db.calendar_event = require("./calendar-event.model")(mongoose); +db.doctemplate = require("./doctemplate.model")(mongoose); +db.exceltemplate = require("./xlsxtemplate.model")(mongoose); +db.timedata = require("./timedata.model")(mongoose); +db.breakfast = require("./breakfast.model")(mongoose); +db.event_request = require("./event-request.model")(mongoose); +db.signature_request = require("./signature-request.model")(mongoose); +db.lunch = require("./lunch.model")(mongoose); +db.snack = require("./snack.model")(mongoose); +module.exports = db; \ No newline at end of file diff --git a/app/models/lunch.model.js b/app/models/lunch.model.js new file mode 100644 index 0000000..6d146c4 --- /dev/null +++ b/app/models/lunch.model.js @@ -0,0 +1,29 @@ +module.exports = mongoose => { + var editHistorySchema = mongoose.Schema({ + employee: String, + date: Date + }); + + var schema = mongoose.Schema( + { + customer_id: String, + customer_name: String, + date: String, + has_lunch: Boolean, + create_by: String, + create_date: Date, + edit_history: [{ + type: editHistorySchema + }], + site: Number + }, + { collection: 'lunch', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + const Lunch = mongoose.model("lunch", schema); + return Lunch; + }; \ No newline at end of file diff --git a/app/models/message-token.model.js b/app/models/message-token.model.js new file mode 100644 index 0000000..adb76df --- /dev/null +++ b/app/models/message-token.model.js @@ -0,0 +1,18 @@ +const uniqueValidator = require('mongoose-unique-validator'); +module.exports = mongoose => { + var schema = mongoose.Schema( + { + message_token: String, + site: Number + }, + { collection: 'message_token', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + schema.plugin(uniqueValidator); + const MessageToken = mongoose.model("message_token", schema); + return MessageToken; + }; \ No newline at end of file diff --git a/app/models/message.model.js b/app/models/message.model.js new file mode 100644 index 0000000..a6d7213 --- /dev/null +++ b/app/models/message.model.js @@ -0,0 +1,22 @@ +const uniqueValidator = require('mongoose-unique-validator'); +module.exports = mongoose => { + var schema = mongoose.Schema( + { + message_name: String, + message_group: Number, + language: String, + message_body: String, + message_title: String, + site: Number + }, + { collection: 'message_template', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + schema.plugin(uniqueValidator); + const Message = mongoose.model("message", schema); + return Message; + }; \ No newline at end of file diff --git a/app/models/report.model.js b/app/models/report.model.js new file mode 100644 index 0000000..f45720e --- /dev/null +++ b/app/models/report.model.js @@ -0,0 +1,39 @@ +const uniqueValidator = require('mongoose-unique-validator'); +module.exports = mongoose => { + var checklistResultSchema = mongoose.Schema({ + item: String, + result: Boolean + }); + var schema = mongoose.Schema( + { + date: String, + route_id: String, + route_name: String, + driver_name: String, + checklist_result: [{ + type: checklistResultSchema + }], + vehicle_number: Number, + type: String, + head: [{ + type: String + }], + chinese_head: [{ + type: String + }], + data: [{ + type: Object + }], + site: Number + }, + { collection: 'report', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + schema.plugin(uniqueValidator); + const Report = mongoose.model("report", schema); + return Report; + }; \ No newline at end of file diff --git a/app/models/resource.model.js b/app/models/resource.model.js new file mode 100644 index 0000000..8544c28 --- /dev/null +++ b/app/models/resource.model.js @@ -0,0 +1,51 @@ +module.exports = mongoose => { + var editHistorySchema = mongoose.Schema({ + employee: String, + date: Date + }); + var schema = mongoose.Schema( + { + name: String, + name_original: String, + name_branch: String, + specialty: String, + type: String, // value may be ['doctor', 'pharmacy' or 'other'] + color: String, + address: String, + city: String, + state: String, + zipcode: String, + phone: String, + status: String, // value might be ['active', 'inactive'] + create_by: String, + create_date: Date, + parent_id: String, + ext_id: String, + category: String, + description: String, + contact: String, + fax: String, + note: String, + data: Object, + edit_by: String, + edit_date: Date, + create_by: String, + create_date: Date, + site: Number, + images: [{ + type: String + }], + edit_history: [{ + type: editHistorySchema + }], + }, + { collection: 'resource', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + const Resource = mongoose.model("resource", schema); + return Resource; + }; \ No newline at end of file diff --git a/app/models/route-path-template.model.js b/app/models/route-path-template.model.js new file mode 100644 index 0000000..c9518a9 --- /dev/null +++ b/app/models/route-path-template.model.js @@ -0,0 +1,45 @@ +module.exports = mongoose => { + var routeCustomerListSchema = mongoose.Schema({ + customer_id: String, + customer_name: String, + customer_address: String, + customer_avatar: String, + customer_group: String, + customer_group_address: String, + customer_type: String, + customer_pickup_status: String, + customer_note: String, + customer_special_needs: String, + customer_phone: String, + customer_enter_center_time: Date, + customer_leave_center_time: Date, + customer_pickup_time: Date, + customer_dropoff_time: Date, + customer_route_status: String, + customer_pickup_order: Number, + customer_table_id: String, + customer_estimated_pickup_time: String, + customer_estimated_dropoff_time: String + }); + var schema = mongoose.Schema( + { + name: String, + vehicle: String, + driver: String, + type: String, + route_customer_list: [{ + type: routeCustomerListSchema + }], + status: String, + site: Number + }, + { collection: 'route_path_template', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + const RoutePathTemplate = mongoose.model("route_path_template", schema); + return RoutePathTemplate; + }; \ No newline at end of file diff --git a/app/models/route-path.model.js b/app/models/route-path.model.js new file mode 100644 index 0000000..6b3c594 --- /dev/null +++ b/app/models/route-path.model.js @@ -0,0 +1,65 @@ +const uniqueValidator = require('mongoose-unique-validator'); +module.exports = mongoose => { + var routeCustomerListSchema = mongoose.Schema({ + customer_id: String, + customer_name: String, + customer_address: String, + customer_avatar: String, + customer_group: String, + customer_group_address: String, + customer_type: String, + customer_pickup_status: String, + customer_note: String, + customer_special_needs: String, + customer_phone: String, + customer_enter_center_time: Date, + customer_leave_center_time: Date, + customer_pickup_time: Date, + customer_dropoff_time: Date, + customer_route_status: String, + customer_pickup_order: Number, + customer_table_id: String, + customer_transfer_to_route: String, + customer_language: String, + customer_estimated_pickup_time: String, + customer_estimated_dropoff_time: String, + customer_address_override: String, + }); + var checklistResultSchema = mongoose.Schema({ + item: String, + result: Boolean + }); + var schema = mongoose.Schema( + { + name: String, + schedule_date: String, + vehicle: String, + status: [{ + type: String + }], + driver: String, + type: String, + start_mileage: Number, + end_mileage: Number, + start_time: Date, + end_time: Date, + estimated_start_time: Date, + route_customer_list: [{ + type: routeCustomerListSchema + }], + checklist_result: [{ + type: checklistResultSchema, + }], + site: Number + }, + { collection: 'route_path', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + schema.plugin(uniqueValidator); + const RoutePath = mongoose.model("route_path", schema); + return RoutePath; + }; \ No newline at end of file diff --git a/app/models/sent-message.model.js b/app/models/sent-message.model.js new file mode 100644 index 0000000..2ec0c1c --- /dev/null +++ b/app/models/sent-message.model.js @@ -0,0 +1,26 @@ +const uniqueValidator = require('mongoose-unique-validator'); +module.exports = mongoose => { + var schema = mongoose.Schema( + { + from_type: String, + from: String, + to_type: String, + to: String, + department: String, + content: String, + status: String, + create_by: String, + create_date: Date, + site: Number + }, + { collection: 'message', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + schema.plugin(uniqueValidator); + const SentMessage = mongoose.model("sentMessage", schema); + return SentMessage; + }; \ No newline at end of file diff --git a/app/models/signature-request.model.js b/app/models/signature-request.model.js new file mode 100644 index 0000000..ddd679a --- /dev/null +++ b/app/models/signature-request.model.js @@ -0,0 +1,23 @@ +module.exports = mongoose => { + var schema = mongoose.Schema( + { + driver_id: String, + driver_name: String, + route_id: String, + route_date: String, + route_name: String, + status: String, + create_by: String, + create_date: Date, + site: Number + }, + { collection: 'signature_request', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + const SignatureRequest = mongoose.model("signature_request", schema); + return SignatureRequest; + }; \ No newline at end of file diff --git a/app/models/snack.model.js b/app/models/snack.model.js new file mode 100644 index 0000000..57dff28 --- /dev/null +++ b/app/models/snack.model.js @@ -0,0 +1,29 @@ +module.exports = mongoose => { + var editHistorySchema = mongoose.Schema({ + employee: String, + date: Date + }); + + var schema = mongoose.Schema( + { + customer_id: String, + customer_name: String, + date: String, + has_snack: Boolean, + create_by: String, + create_date: Date, + edit_history: [{ + type: editHistorySchema + }], + site: Number + }, + { collection: 'snack', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + const Snack = mongoose.model("snack", schema); + return Snack; + }; \ No newline at end of file diff --git a/app/models/staff.model.js b/app/models/staff.model.js new file mode 100644 index 0000000..58da53a --- /dev/null +++ b/app/models/staff.model.js @@ -0,0 +1,63 @@ +const uniqueValidator = require('mongoose-unique-validator'); +module.exports = mongoose => { + var schema = mongoose.Schema( + { + username: { + type: String, + required: true, + unique: true + }, + name: String, + name_cn: String, + email: { + type: String, + required: true, + unique: true + }, + password: String, + roles: [{ + type: String + }], + mobile_phone: String, + phone: String, + home_phone: String, + language: String, + employment_status: String, + status: String, + title: String, + title_cn: String, + firstname: String, + lastname: String, + department: String, + birth_date: String, + driver_capacity: Number, + date_hired: String, + create_by: String, + create_date: Date, + edit_by: String, + edit_date: Date, + note: Object, + salt: String, + city: String, + state: String, + zipcode: String, + group: [{ + type: String + }], + tags: [{ + type: String + }], + parent_id: String, + site: Number + }, + { collection: 'staff', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + schema.plugin(uniqueValidator); + const Staff = mongoose.model("staff", schema); + return Staff; + }; \ No newline at end of file diff --git a/app/models/timedata.model.js b/app/models/timedata.model.js new file mode 100644 index 0000000..0a34670 --- /dev/null +++ b/app/models/timedata.model.js @@ -0,0 +1,25 @@ +module.exports = mongoose => { + var schema = mongoose.Schema( + { + time: Date, + translate1_number: Number, + translate2_number: Number, + max_resource_number: Number, + status: String, // 'active', 'inactive' + data: Object, + site: Number, + create_by: String, + create_date: Date, + edit_by: String, + edit_date: Date + }, + { collection: 'timedata', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + const Timedata = mongoose.model("timedata", schema); + return Timedata; + }; \ No newline at end of file diff --git a/app/models/user.model.js b/app/models/user.model.js new file mode 100644 index 0000000..336d927 --- /dev/null +++ b/app/models/user.model.js @@ -0,0 +1,26 @@ +module.exports = mongoose => { + var schema = mongoose.Schema( + { + username: String, + email: String, + salt: Number, + password: String, + api_token: String, + roles: [{ + type: String + }], + status: String, + create_by: String, + create_date: Date, + site: Number + }, + { collection: 'user', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + const User = mongoose.model("user", schema); + return User; + }; \ No newline at end of file diff --git a/app/models/vehicle.model.js b/app/models/vehicle.model.js new file mode 100644 index 0000000..b64a94c --- /dev/null +++ b/app/models/vehicle.model.js @@ -0,0 +1,40 @@ +const uniqueValidator = require('mongoose-unique-validator'); +module.exports = mongoose => { + var schema = mongoose.Schema( + { + vehicle_number: { + type: Number, + required: true, + }, + tag: { + type: String, + required: true + }, + ezpass: { + type: String, + }, + gps_tag: { + type: String, + }, + mileage: Number, + capacity: Number, + make: String, + vehicle_model: String, + year: String, + checklist: [{ + type: String + }], + status: String, + site: Number + }, + { collection: 'vehicle', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + schema.index({tag: 1, site:1}, {unique: true}); + const Vehicle = mongoose.model("vehicle", schema); + return Vehicle; + }; \ No newline at end of file diff --git a/app/models/xlsxtemplate.model.js b/app/models/xlsxtemplate.model.js new file mode 100644 index 0000000..797040e --- /dev/null +++ b/app/models/xlsxtemplate.model.js @@ -0,0 +1,37 @@ +module.exports = mongoose => { + var editHistorySchema = mongoose.Schema({ + employee: String, + date: Date + }); + var schema = mongoose.Schema( + { + name: { + type: String, + required: true, + }, + content: String, + description: String, + model: Object, + status: String, // 'active', 'inactive' + file: [{ + type: Object + }], + site: Number, + create_by: String, + create_date: Date, + edit_by: String, + edit_date: Date, + edit_history: [{ + type: editHistorySchema + }], + }, + { collection: 'xlsx-template', timestamps: true } + ); + schema.method("toJSON", function() { + const { __v, _id, ...object } = this.toObject(); + object.id = _id; + return object; + }); + const ExcelTemplate = mongoose.model("xlsxtemplate", schema); + return ExcelTemplate; + }; \ No newline at end of file diff --git a/app/routes/.DS_Store b/app/routes/.DS_Store new file mode 100644 index 0000000..b7f0363 Binary files /dev/null and b/app/routes/.DS_Store differ diff --git a/app/routes/auth.routes.js b/app/routes/auth.routes.js new file mode 100644 index 0000000..93538f6 --- /dev/null +++ b/app/routes/auth.routes.js @@ -0,0 +1,11 @@ +module.exports = app => { + const auth = require("../controllers/auth.controller"); + app.use(function(req, res, next) { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + app.post('/api/auth/login', auth.login); + }; \ No newline at end of file diff --git a/app/routes/breakfast.routes.js b/app/routes/breakfast.routes.js new file mode 100644 index 0000000..44f4bb1 --- /dev/null +++ b/app/routes/breakfast.routes.js @@ -0,0 +1,19 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const breakfast = require("../controllers/breakfast.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Retrieve all breakfasts + router.get("/", [authJwt.verifyToken], breakfast.getAllBreakfasts); + // Create a new breakfast + router.post("/", [authJwt.verifyToken], breakfast.createNewBreakfast); + router.put('/:id', [authJwt.verifyToken], breakfast.updateBreakfast); + router.delete('/:id', [authJwt.verifyToken], breakfast.deleteBreakfast) + app.use('/api/breakfasts', router); +}; \ No newline at end of file diff --git a/app/routes/calendar-event.route.js b/app/routes/calendar-event.route.js new file mode 100644 index 0000000..bb4a96b --- /dev/null +++ b/app/routes/calendar-event.route.js @@ -0,0 +1,23 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const calendarEvent = require("../controllers/calendar-event.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Retrieve all Events + router.get("/", [authJwt.verifyToken], calendarEvent.getAllEvents); + router.get('/getByCustomer',[authJwt.verifyToken], calendarEvent.getEventsByCustomer ); + // Create a new Event + router.post("/", [authJwt.verifyToken], calendarEvent.createCalendarEvent); + router.get('/:id', [authJwt.verifyToken], calendarEvent.getEvent); + router.put('/:id', [authJwt.verifyToken], calendarEvent.updateEvent); + router.put('/:id/disable', [authJwt.verifyToken], calendarEvent.disableEvent); + router.post('/assign', [authJwt.verifyToken], calendarEvent.assignTransportationToEvents); + router.delete('/:id', [authJwt.verifyToken], calendarEvent.deleteEvent); + app.use('/api/events', router); + }; \ No newline at end of file diff --git a/app/routes/center-phone.routes.js b/app/routes/center-phone.routes.js new file mode 100644 index 0000000..26934ac --- /dev/null +++ b/app/routes/center-phone.routes.js @@ -0,0 +1,19 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const centerPhone = require("../controllers/center-phone.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Retrieve all Phones + router.get("/", [authJwt.verifyToken], centerPhone.getAllCenterPhones); + // Create a new Phone + router.post("/", [authJwt.verifyToken], centerPhone.createCenterPhone); + router.get('/:id', [authJwt.verifyToken], centerPhone.getCenterPhone); + router.put('/:id', [authJwt.verifyToken], centerPhone.updateCenterPhone); + app.use('/api/phones', router); + }; \ No newline at end of file diff --git a/app/routes/client.routes.js b/app/routes/client.routes.js new file mode 100644 index 0000000..e221e6a --- /dev/null +++ b/app/routes/client.routes.js @@ -0,0 +1,20 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const clients = require("../controllers/client.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Create a new client + router.post("/",[authJwt.verifyToken], clients.createClient); + router.get("/:id",[authJwt.verifyToken], clients.getClient); + // Update Client + router.put("/:id",[authJwt.verifyToken], clients.updateClient ); + // Get clients with name or email + router.get("/search", [authJwt.verifyToken], clients.getClientsWithNameOrEmail); + app.use('/api/clients', router); + }; \ No newline at end of file diff --git a/app/routes/customer.routes.js b/app/routes/customer.routes.js new file mode 100644 index 0000000..416c691 --- /dev/null +++ b/app/routes/customer.routes.js @@ -0,0 +1,24 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const customers = require("../controllers/customer.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Retrieve all customer + router.get("/", [authJwt.verifyToken], customers.getAllCustomers); + // Get All active customers + router.get("/active",[authJwt.verifyToken], customers.getAllActiveCustomers); + // Create a new customer + router.post("/",[authJwt.verifyToken], customers.createCustomer); + // Get Customers with name or email + router.get("/search", [authJwt.verifyToken], customers.getCustomersWithNameOrEmail); + // Update Customer + router.put("/:id",[authJwt.verifyToken], customers.updateCustomer ); + router.get("/:id", [authJwt.verifyToken], customers.getCustomer); + app.use('/api/customers', router); + }; \ No newline at end of file diff --git a/app/routes/doctemplate.route.js b/app/routes/doctemplate.route.js new file mode 100644 index 0000000..c8c0e67 --- /dev/null +++ b/app/routes/doctemplate.route.js @@ -0,0 +1,9 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const docTemplates = require("../controllers/doctemplate.controller.js"); + var router = require("express").Router(); + // Retrieve all users + router.get("/get-docs", docTemplates.createDocFromDocTemplateName); + router.get("/get-pdfs", docTemplates.createPDFFromDocTemplateName); + app.use('/api/docs', router); + }; \ No newline at end of file diff --git a/app/routes/employee.routes.js b/app/routes/employee.routes.js new file mode 100644 index 0000000..1890437 --- /dev/null +++ b/app/routes/employee.routes.js @@ -0,0 +1,21 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const employees = require("../controllers/employee.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Retrieve all employee + router.get("/",[authJwt.verifyToken], employees.getAllEmployees); + // Create a new employee + router.post("/", employees.createEmployee); + // Update an employee + router.put("/:id",[authJwt.verifyToken], employees.updateEmployee ); + // Get one employee + router.get("/:id",[authJwt.verifyToken], employees.getEmployee) + app.use('/api/employees', router); + }; \ No newline at end of file diff --git a/app/routes/event-request.routes.js b/app/routes/event-request.routes.js new file mode 100644 index 0000000..7a51d2b --- /dev/null +++ b/app/routes/event-request.routes.js @@ -0,0 +1,19 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const eventRequest = require("../controllers/event-request.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Retrieve all event Request + router.get("/", [authJwt.verifyToken], eventRequest.getAllEventRequests); + // Create a new event Request + router.post("/", [authJwt.verifyToken], eventRequest.createEventRequest); + router.put('/:id', [authJwt.verifyToken], eventRequest.updateRequestItem); + router.delete('/:id', [authJwt.verifyToken], eventRequest.deleteEventRequest) + app.use('/api/event-requests', router); +}; \ No newline at end of file diff --git a/app/routes/lunch.routes.js b/app/routes/lunch.routes.js new file mode 100644 index 0000000..e3eb4ad --- /dev/null +++ b/app/routes/lunch.routes.js @@ -0,0 +1,19 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const lunch = require("../controllers/lunch.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Retrieve all lunches + router.get("/", [authJwt.verifyToken], lunch.getAllLunches); + // Create a new Lunch + router.post("/", [authJwt.verifyToken], lunch.createNewLunch); + router.put('/:id', [authJwt.verifyToken], lunch.updateLunch); + router.delete('/:id', [authJwt.verifyToken], lunch.deleteLunch) + app.use('/api/lunches', router); +}; \ No newline at end of file diff --git a/app/routes/message-token.routes.js b/app/routes/message-token.routes.js new file mode 100644 index 0000000..fbd8d74 --- /dev/null +++ b/app/routes/message-token.routes.js @@ -0,0 +1,19 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const messageTokens = require("../controllers/message-token.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Retrieve all messageTokens + router.get("/",[authJwt.verifyToken], messageTokens.getAllMessageTokens); + // Create a new messageToken + router.post("/", [authJwt.verifyToken], messageTokens.createMessageToken); + // Update an messageToken + router.put("/:id",[authJwt.verifyToken], messageTokens.updateMessageToken ); + app.use('/api/message-tokens', router); + }; \ No newline at end of file diff --git a/app/routes/message.routes.js b/app/routes/message.routes.js new file mode 100644 index 0000000..3b1789d --- /dev/null +++ b/app/routes/message.routes.js @@ -0,0 +1,25 @@ +const {authJwt} = require("../middlewares"); + +module.exports = app => { + const messages = require("../controllers/message.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Retrieve all messages + router.get("/",[authJwt.verifyToken], messages.getAllMessages); + // Create a new message + router.post("/", [authJwt.verifyToken], messages.createMessage); + // Update an message + router.put("/:id",[authJwt.verifyToken], messages.updateMessage ); + // Get messages by Date and Type + router.get("/:id", [authJwt.verifyToken], messages.getMessage); + router.get("/sent-messages/all", [authJwt.verifyToken], messages.getAllSentMessages); + router.get("/public/search", messages.getMessagesByGroupAndLanguage); + router.post("/public/send", [authJwt.verifyToken], messages.sendMessage); + app.use('/api/messages', router); + }; \ No newline at end of file diff --git a/app/routes/report.routes.js b/app/routes/report.routes.js new file mode 100644 index 0000000..b1920fa --- /dev/null +++ b/app/routes/report.routes.js @@ -0,0 +1,22 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const reports = require("../controllers/report.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Retrieve all reports + router.get("/",[authJwt.verifyToken], reports.getAllReports); + // Create a new report + router.post("/", [authJwt.verifyToken], reports.createReport); + // Update an report + router.put("/:id",[authJwt.verifyToken], reports.updateReport ); + // Get reports by Date and Type + router.get("/search", reports.getReportsByDateAndType); + router.get("/search-route", reports.getReportsByRouteIdAndType); + app.use('/api/reports', router); + }; \ No newline at end of file diff --git a/app/routes/resource.routes.js b/app/routes/resource.routes.js new file mode 100644 index 0000000..6a6746c --- /dev/null +++ b/app/routes/resource.routes.js @@ -0,0 +1,14 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const resource = require("../controllers/resource.controller.js"); + var router = require("express").Router(); + // Retrieve all resources + router.get("/", [authJwt.verifyToken], resource.getAllResources); + // Create a new resource + router.post("/", [authJwt.verifyToken], resource.createResource); + router.get('/:id', [authJwt.verifyToken], resource.getResource); + router.put('/:id', [authJwt.verifyToken], resource.updateResource); + router.put('/:id/disable', [authJwt.verifyToken], resource.disableResource); + router.delete('/:id', [authJwt.verifyToken], resource.deleteResource) + app.use('/api/resources', router); + }; \ No newline at end of file diff --git a/app/routes/route-path-template.routes.js b/app/routes/route-path-template.routes.js new file mode 100644 index 0000000..ed5aa4e --- /dev/null +++ b/app/routes/route-path-template.routes.js @@ -0,0 +1,22 @@ +const { route } = require("express/lib/application"); +const {authJwt} = require("../middlewares"); +module.exports = app => { + const routePathTemplates = require("../controllers/route-path-template.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Retrieve all routes + router.get("/", [authJwt.verifyToken],routePathTemplates.getAllRoutesTemplates); + // Create a new route + router.post("/", [authJwt.verifyToken], routePathTemplates.createRoutePathTemplate); + // Update a route with id + router.put("/:id", [authJwt.verifyToken], routePathTemplates.updateRouteTemplate); + // Delete a route with id + router.delete("/:id", [authJwt.verifyToken], routePathTemplates.deleteRouteTemplate); + app.use('/api/route-templates', router); + }; \ No newline at end of file diff --git a/app/routes/route-path.routes.js b/app/routes/route-path.routes.js new file mode 100644 index 0000000..76b232a --- /dev/null +++ b/app/routes/route-path.routes.js @@ -0,0 +1,25 @@ +const { route } = require("express/lib/application"); +const {authJwt} = require("../middlewares"); +module.exports = app => { + const routePaths = require("../controllers/route-path.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Retrieve all routes + router.get("/", [authJwt.verifyToken], routePaths.getAllRoutes); + router.get("/:id", [authJwt.verifyToken], routePaths.getRoute); + // Create a new route + router.post("/", [authJwt.verifyToken], routePaths.createRoutePath); + // Update a route with id + router.put("/:id", [authJwt.verifyToken], routePaths.updateRoute); + // Delete a route with id + router.delete("/:id", [authJwt.verifyToken], routePaths.deleteRoute); + router.post("/update-in-progress", [authJwt.verifyToken], routePaths.updateRouteInProgress); + router.get("/routes-with-phone", routePaths.getAllRoutesWithPhones); + app.use('/api/routes', router); + }; \ No newline at end of file diff --git a/app/routes/signature-request.routes.js b/app/routes/signature-request.routes.js new file mode 100644 index 0000000..ddcd545 --- /dev/null +++ b/app/routes/signature-request.routes.js @@ -0,0 +1,23 @@ +const { route } = require("express/lib/application"); +module.exports = app => { + const signatureRequest = require("../controllers/signature-request.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Retrieve all signature requests + router.get("/",signatureRequest.getAllSignatureRequests); + // Get one signature request by id + router.get("/:id", signatureRequest.getSignatureRequest); + // Create a new signature request + router.post("/", signatureRequest.createNewSignatureRequest); + // Update a signature request with id + router.put("/:id", signatureRequest.updateSignatureRequest); + // Delete a signature request with id + router.delete("/:id", signatureRequest.deleteSignatureRequest); + app.use('/api/signature-requests', router); + }; \ No newline at end of file diff --git a/app/routes/snack.routes.js b/app/routes/snack.routes.js new file mode 100644 index 0000000..c6c65aa --- /dev/null +++ b/app/routes/snack.routes.js @@ -0,0 +1,19 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const snack = require("../controllers/snack.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Retrieve all snacks + router.get("/", [authJwt.verifyToken], snack.getAllSnacks); + // Create a new snack + router.post("/", [authJwt.verifyToken], snack.createNewSnack); + router.put('/:id', [authJwt.verifyToken], snack.updateSnack); + router.delete('/:id', [authJwt.verifyToken], snack.deleteSnack) + app.use('/api/snacks', router); +}; \ No newline at end of file diff --git a/app/routes/staff.routes.js b/app/routes/staff.routes.js new file mode 100644 index 0000000..260763e --- /dev/null +++ b/app/routes/staff.routes.js @@ -0,0 +1,19 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const staffs = require("../controllers/staff.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Create a new staff + router.post("/", staffs.createStaff); + // Update an Staff + router.put("/:id",[authJwt.verifyToken], staffs.updateStaff ); + // Get Staff by email or username + router.get("/search", [authJwt.verifyToken], staffs.getStaffsWithNameOrEmail) + app.use('/api/staffs', router); + }; \ No newline at end of file diff --git a/app/routes/timedata.routes.js b/app/routes/timedata.routes.js new file mode 100644 index 0000000..04cff1f --- /dev/null +++ b/app/routes/timedata.routes.js @@ -0,0 +1,9 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const timedata = require("../controllers/timedata.controller"); + var router = require("express").Router(); + // Retrieve all users + router.get("/get-by-condition1", timedata.getDocsByCondition1); + + app.use('/api/timedata', router); +}; \ No newline at end of file diff --git a/app/routes/upload.routes.js b/app/routes/upload.routes.js new file mode 100644 index 0000000..20c4f59 --- /dev/null +++ b/app/routes/upload.routes.js @@ -0,0 +1,8 @@ +module.exports = app => { + const upload = require("../controllers/upload.controller.js"); + var router = require("express").Router(); + router.get("/:name", upload.getFile); + router.post("/upload/:filename", upload.uploadFiles); + router.post("/delete", upload.deleteFile); + app.use('/api/files', router); + }; \ No newline at end of file diff --git a/app/routes/user.routes.js b/app/routes/user.routes.js new file mode 100644 index 0000000..386ad0d --- /dev/null +++ b/app/routes/user.routes.js @@ -0,0 +1,7 @@ +module.exports = app => { + const user = require("../controllers/user.controller.js"); + var router = require("express").Router(); + // Retrieve all users + router.get("/", user.getAllUsers); + app.use('/api/users', router); + }; \ No newline at end of file diff --git a/app/routes/vehicle.routes.js b/app/routes/vehicle.routes.js new file mode 100644 index 0000000..86b1c17 --- /dev/null +++ b/app/routes/vehicle.routes.js @@ -0,0 +1,21 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const vehicles = require("../controllers/vehicle.controller.js"); + app.use((req, res, next) => { + res.header( + "Access-Control-Allow-Headers", + "x-access-token, Origin, Content-Type, Accept" + ); + next(); + }); + var router = require("express").Router(); + // Retrieve all employee + router.get("/", [authJwt.verifyToken], vehicles.getAllVehicles); + // Create a new employee + router.post("/", [authJwt.verifyToken], vehicles.createVehicle); + router.get('/active', [authJwt.verifyToken], vehicles.getAllActiveVehicles); + router.get('/:id', [authJwt.verifyToken], vehicles.getVehicle); + router.put('/:id', [authJwt.verifyToken], vehicles.updateVehicle); + router.delete('/:id', [authJwt.verifyToken], vehicles.deleteVehicle); + app.use('/api/vehicles', router); + }; \ No newline at end of file diff --git a/app/routes/xlsxtemplate.route.js b/app/routes/xlsxtemplate.route.js new file mode 100644 index 0000000..1cb29b2 --- /dev/null +++ b/app/routes/xlsxtemplate.route.js @@ -0,0 +1,9 @@ +const {authJwt} = require("../middlewares"); +module.exports = app => { + const excelTemplates = require("../controllers/xlsxtemplate.controller"); + var router = require("express").Router(); + // Retrieve all users + router.get("/get-sheets", excelTemplates.createSheetFromTemplateName); + router.get("/get-pdf", excelTemplates.createPDFFromSheetTemplateName); + app.use('/api/sheets', router); + }; \ No newline at end of file diff --git a/app/upload/.DS_Store b/app/upload/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/app/upload/.DS_Store differ diff --git a/app/upload/module-file-1620410222358 b/app/upload/module-file-1620410222358 new file mode 100644 index 0000000..d3ad333 Binary files /dev/null and b/app/upload/module-file-1620410222358 differ diff --git a/app/upload/module-file-1634054180752 b/app/upload/module-file-1634054180752 new file mode 100644 index 0000000..6dfa702 Binary files /dev/null and b/app/upload/module-file-1634054180752 differ diff --git a/app/upload/module-file-1646425098919 b/app/upload/module-file-1646425098919 new file mode 100644 index 0000000..a17b3f5 Binary files /dev/null and b/app/upload/module-file-1646425098919 differ diff --git a/app/views/.DS_Store b/app/views/.DS_Store new file mode 100644 index 0000000..a86d637 Binary files /dev/null and b/app/views/.DS_Store differ diff --git a/app/views/asset-manifest.json b/app/views/asset-manifest.json new file mode 100644 index 0000000..ceea62e --- /dev/null +++ b/app/views/asset-manifest.json @@ -0,0 +1,16 @@ +{ + "files": { + "main.css": "/static/css/main.22055773.css", + "main.js": "/static/js/main.3884af74.js", + "static/js/787.c4e7f8f9.chunk.js": "/static/js/787.c4e7f8f9.chunk.js", + "static/media/background.jpg": "/static/media/background.d0e107221150b4c16901.jpg", + "index.html": "/index.html", + "main.22055773.css.map": "/static/css/main.22055773.css.map", + "main.3884af74.js.map": "/static/js/main.3884af74.js.map", + "787.c4e7f8f9.chunk.js.map": "/static/js/787.c4e7f8f9.chunk.js.map" + }, + "entrypoints": [ + "static/css/main.22055773.css", + "static/js/main.3884af74.js" + ] +} \ No newline at end of file diff --git a/app/views/favicon-16x16.png b/app/views/favicon-16x16.png new file mode 100755 index 0000000..2e95d26 Binary files /dev/null and b/app/views/favicon-16x16.png differ diff --git a/app/views/favicon-32x32.png b/app/views/favicon-32x32.png new file mode 100755 index 0000000..e9e66b4 Binary files /dev/null and b/app/views/favicon-32x32.png differ diff --git a/app/views/favicon.ico b/app/views/favicon.ico new file mode 100755 index 0000000..b5e62ac Binary files /dev/null and b/app/views/favicon.ico differ diff --git a/app/views/images/.DS_Store b/app/views/images/.DS_Store new file mode 100644 index 0000000..7139381 Binary files /dev/null and b/app/views/images/.DS_Store differ diff --git a/app/views/images/background.jpg b/app/views/images/background.jpg new file mode 100644 index 0000000..104f582 Binary files /dev/null and b/app/views/images/background.jpg differ diff --git a/app/views/images/default.png b/app/views/images/default.png new file mode 100644 index 0000000..af5bc7c Binary files /dev/null and b/app/views/images/default.png differ diff --git a/app/views/images/down_arrow.png b/app/views/images/down_arrow.png new file mode 100644 index 0000000..0e156de Binary files /dev/null and b/app/views/images/down_arrow.png differ diff --git a/app/views/images/drag-and-drop.svg b/app/views/images/drag-and-drop.svg new file mode 100644 index 0000000..e0dd938 --- /dev/null +++ b/app/views/images/drag-and-drop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/views/images/logo.png b/app/views/images/logo.png new file mode 100644 index 0000000..21c8255 Binary files /dev/null and b/app/views/images/logo.png differ diff --git a/app/views/images/logo1.png b/app/views/images/logo1.png new file mode 100644 index 0000000..c101a5d Binary files /dev/null and b/app/views/images/logo1.png differ diff --git a/app/views/images/logo2.png b/app/views/images/logo2.png new file mode 100644 index 0000000..88581e4 Binary files /dev/null and b/app/views/images/logo2.png differ diff --git a/app/views/images/logo3.png b/app/views/images/logo3.png new file mode 100644 index 0000000..abd3e26 Binary files /dev/null and b/app/views/images/logo3.png differ diff --git a/app/views/images/logo_new.jpg b/app/views/images/logo_new.jpg new file mode 100644 index 0000000..bf07a92 Binary files /dev/null and b/app/views/images/logo_new.jpg differ diff --git a/app/views/images/signature.jpeg b/app/views/images/signature.jpeg new file mode 100644 index 0000000..49527a7 Binary files /dev/null and b/app/views/images/signature.jpeg differ diff --git a/app/views/images/up_arrow.png b/app/views/images/up_arrow.png new file mode 100644 index 0000000..e1ba61a Binary files /dev/null and b/app/views/images/up_arrow.png differ diff --git a/app/views/index.html b/app/views/index.html new file mode 100644 index 0000000..7089648 --- /dev/null +++ b/app/views/index.html @@ -0,0 +1 @@ +Worldshine Transportation
\ No newline at end of file diff --git a/app/views/logo.png b/app/views/logo.png new file mode 100644 index 0000000..21c8255 Binary files /dev/null and b/app/views/logo.png differ diff --git a/app/views/logo192.png b/app/views/logo192.png new file mode 100755 index 0000000..197d491 Binary files /dev/null and b/app/views/logo192.png differ diff --git a/app/views/logo512.png b/app/views/logo512.png new file mode 100755 index 0000000..504e175 Binary files /dev/null and b/app/views/logo512.png differ diff --git a/app/views/manifest.json b/app/views/manifest.json new file mode 100644 index 0000000..080d6c7 --- /dev/null +++ b/app/views/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/app/views/robots.txt b/app/views/robots.txt new file mode 100644 index 0000000..e9e57dc --- /dev/null +++ b/app/views/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/app/views/static/css/main.22055773.css b/app/views/static/css/main.22055773.css new file mode 100644 index 0000000..2c2a11d --- /dev/null +++ b/app/views/static/css/main.22055773.css @@ -0,0 +1,7 @@ +@charset "UTF-8";body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}/*! + * Bootstrap v5.1.3 (https://getbootstrap.com/) + * Copyright 2011-2021 The Bootstrap Authors + * Copyright 2011-2021 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-body-color-rgb:33,37,41;--bs-body-bg-rgb:255,255,255;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg,hsla(0,0%,100%,.15),hsla(0,0%,100%,0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-bg:#fff}*,:after,:before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent;background-color:#fff;background-color:var(--bs-body-bg);color:#212529;color:var(--bs-body-color);font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,Liberation Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-family:var(--bs-body-font-family);font-size:1rem;font-size:var(--bs-body-font-size);font-weight:400;font-weight:var(--bs-body-font-weight);line-height:1.5;line-height:var(--bs-body-line-height);margin:0;text-align:var(--bs-body-text-align)}hr{background-color:currentColor;border:0;color:inherit;margin:1rem 0;opacity:.25}hr:not([size]){height:1px}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-weight:500;line-height:1.2;margin-bottom:.5rem;margin-top:0}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-bottom:1rem;margin-top:0}abbr[data-bs-original-title],abbr[title]{cursor:help;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{font-style:normal;line-height:inherit;margin-bottom:1rem}ol,ul{padding-left:2rem}dl,ol,ul{margin-bottom:1rem;margin-top:0}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{background-color:#fcf8e3;padding:.2em}sub,sup{font-size:.75em;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{direction:ltr;font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-family:var(--bs-font-monospace);font-size:1em;unicode-bidi:bidi-override}pre{display:block;font-size:.875em;margin-bottom:1rem;margin-top:0;overflow:auto}pre code{color:inherit;font-size:inherit;word-break:normal}code{word-wrap:break-word;color:#d63384;font-size:.875em}a>code{color:inherit}kbd{background-color:#212529;border-radius:.2rem;color:#fff;font-size:.875em;padding:.2rem .4rem}kbd kbd{font-size:1em;font-weight:700;padding:0}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{border-collapse:collapse;caption-side:bottom}caption{color:#6c757d;padding-bottom:.5rem;padding-top:.5rem;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border:0 solid;border-color:inherit}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit;margin:0}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{border-style:none;padding:0}textarea{resize:vertical}fieldset{border:0;margin:0;min-width:0;padding:0}legend{float:left;font-size:calc(1.275rem + .3vw);line-height:inherit;margin-bottom:.5rem;padding:0;width:100%}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}output{display:inline-block}iframe{border:0}summary{cursor:pointer;display:list-item}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-inline,.list-unstyled{list-style:none;padding-left:0}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{font-size:1.25rem;margin-bottom:1rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{color:#6c757d;font-size:.875em;margin-bottom:1rem;margin-top:-1rem}.blockquote-footer:before{content:"— "}.img-fluid,.img-thumbnail{height:auto;max-width:100%}.img-thumbnail{background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;padding:.25rem}.figure{display:inline-block}.figure-img{line-height:1;margin-bottom:.5rem}.figure-caption{color:#6c757d;font-size:.875em}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{margin-left:auto;margin-right:auto;padding-left:.75rem;padding-left:var(--bs-gutter-x,.75rem);padding-right:.75rem;padding-right:var(--bs-gutter-x,.75rem);width:100%}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-left:calc(var(--bs-gutter-x)*-.5);margin-right:calc(var(--bs-gutter-x)*-.5);margin-top:calc(var(--bs-gutter-y)*-1)}.row>*{flex-shrink:0;margin-top:var(--bs-gutter-y);max-width:100%;padding-left:calc(var(--bs-gutter-x)*.5);padding-right:calc(var(--bs-gutter-x)*.5);width:100%}.col{flex:1 0}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-bg:transparent;--bs-table-accent-bg:transparent;--bs-table-striped-color:#212529;--bs-table-striped-bg:rgba(0,0,0,.05);--bs-table-active-color:#212529;--bs-table-active-bg:rgba(0,0,0,.1);--bs-table-hover-color:#212529;--bs-table-hover-bg:rgba(0,0,0,.075);border-color:#dee2e6;color:#212529;margin-bottom:1rem;vertical-align:top;width:100%}.table>:not(caption)>*>*{background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg);padding:.5rem}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table>:not(:first-child){border-top:2px solid}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem}.table-bordered>:not(caption)>*{border-width:1px 0}.table-bordered>:not(caption)>*>*{border-width:0 1px}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover>*{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-bg:#cfe2ff;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;border-color:#bacbe6;color:#000}.table-secondary{--bs-table-bg:#e2e3e5;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;border-color:#cbccce;color:#000}.table-success{--bs-table-bg:#d1e7dd;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;border-color:#bcd0c7;color:#000}.table-info{--bs-table-bg:#cff4fc;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;border-color:#badce3;color:#000}.table-warning{--bs-table-bg:#fff3cd;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;border-color:#e6dbb9;color:#000}.table-danger{--bs-table-bg:#f8d7da;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;border-color:#dfc2c4;color:#000}.table-light{--bs-table-bg:#f8f9fa;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;border-color:#dfe0e1;color:#000}.table-dark{--bs-table-bg:#212529;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;border-color:#373b3e;color:#fff}.table-responsive{-webkit-overflow-scrolling:touch;overflow-x:auto}@media (max-width:575.98px){.table-responsive-sm{-webkit-overflow-scrolling:touch;overflow-x:auto}}@media (max-width:767.98px){.table-responsive-md{-webkit-overflow-scrolling:touch;overflow-x:auto}}@media (max-width:991.98px){.table-responsive-lg{-webkit-overflow-scrolling:touch;overflow-x:auto}}@media (max-width:1199.98px){.table-responsive-xl{-webkit-overflow-scrolling:touch;overflow-x:auto}}@media (max-width:1399.98px){.table-responsive-xxl{-webkit-overflow-scrolling:touch;overflow-x:auto}}.form-label{margin-bottom:.5rem}.col-form-label{font-size:inherit;line-height:1.5;margin-bottom:0;padding-bottom:calc(.375rem + 1px);padding-top:calc(.375rem + 1px)}.col-form-label-lg{font-size:1.25rem;padding-bottom:calc(.5rem + 1px);padding-top:calc(.5rem + 1px)}.col-form-label-sm{font-size:.875rem;padding-bottom:calc(.25rem + 1px);padding-top:calc(.25rem + 1px)}.form-text{color:#6c757d;font-size:.875em;margin-top:.25rem}.form-control{-webkit-appearance:none;appearance:none;background-clip:padding-box;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem;color:#212529;display:block;font-size:1rem;font-weight:400;line-height:1.5;padding:.375rem .75rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;width:100%}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{background-color:#fff;border-color:#86b7fe;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);color:#212529;outline:0}.form-control::-webkit-date-and-time-value{height:1.5em}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}.form-control::file-selector-button{-webkit-margin-end:.75rem;background-color:#e9ecef;border:0 solid;border-color:inherit;border-inline-end-width:1px;border-radius:0;color:#212529;margin:-.375rem -.75rem;margin-inline-end:.75rem;padding:.375rem .75rem;pointer-events:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3}.form-control::-webkit-file-upload-button{-webkit-margin-end:.75rem;background-color:#e9ecef;border:0 solid;border-color:inherit;border-inline-end-width:1px;border-radius:0;color:#212529;margin:-.375rem -.75rem;margin-inline-end:.75rem;padding:.375rem .75rem;pointer-events:none;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control-plaintext{background-color:transparent;border:solid transparent;border-width:1px 0;color:#212529;display:block;line-height:1.5;margin-bottom:0;padding:.375rem 0;width:100%}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-left:0;padding-right:0}.form-control-sm{border-radius:.2rem;font-size:.875rem;min-height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem}.form-control-sm::file-selector-button{-webkit-margin-end:.5rem;margin:-.25rem -.5rem;margin-inline-end:.5rem;padding:.25rem .5rem}.form-control-sm::-webkit-file-upload-button{-webkit-margin-end:.5rem;margin:-.25rem -.5rem;margin-inline-end:.5rem;padding:.25rem .5rem}.form-control-lg{border-radius:.3rem;font-size:1.25rem;min-height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem}.form-control-lg::file-selector-button{-webkit-margin-end:1rem;margin:-.5rem -1rem;margin-inline-end:1rem;padding:.5rem 1rem}.form-control-lg::-webkit-file-upload-button{-webkit-margin-end:1rem;margin:-.5rem -1rem;margin-inline-end:1rem;padding:.5rem 1rem}textarea.form-control{min-height:calc(1.5em + .75rem + 2px)}textarea.form-control-sm{min-height:calc(1.5em + .5rem + 2px)}textarea.form-control-lg{min-height:calc(1.5em + 1rem + 2px)}.form-control-color{height:auto;padding:.375rem;width:3rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border-radius:.25rem;height:1.5em}.form-control-color::-webkit-color-swatch{border-radius:.25rem;height:1.5em}.form-select{-moz-padding-start:calc(.75rem - 3px);-webkit-appearance:none;appearance:none;background-color:#fff;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3E%3C/svg%3E");background-position:right .75rem center;background-repeat:no-repeat;background-size:16px 12px;border:1px solid #ced4da;border-radius:.25rem;color:#212529;display:block;font-size:1rem;font-weight:400;line-height:1.5;padding:.375rem 2.25rem .375rem .75rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;width:100%}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);outline:0}.form-select[multiple],.form-select[size]:not([size="1"]){background-image:none;padding-right:.75rem}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #212529}.form-select-sm{border-radius:.2rem;font-size:.875rem;padding-bottom:.25rem;padding-left:.5rem;padding-top:.25rem}.form-select-lg{border-radius:.3rem;font-size:1.25rem;padding-bottom:.5rem;padding-left:1rem;padding-top:.5rem}.form-check{display:block;margin-bottom:.125rem;min-height:1.5rem;padding-left:1.5em}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-input{color-adjust:exact;-webkit-appearance:none;appearance:none;background-color:#fff;background-position:50%;background-repeat:no-repeat;background-size:contain;border:1px solid rgba(0,0,0,.25);height:1em;margin-top:.25em;-webkit-print-color-adjust:exact;vertical-align:top;width:1em}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{-webkit-filter:brightness(90%);filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);outline:0}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3E%3C/svg%3E")}.form-check-input:checked[type=radio]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='2' fill='%23fff'/%3E%3C/svg%3E")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3E%3C/svg%3E");border-color:#0d6efd}.form-check-input:disabled{-webkit-filter:none;filter:none;opacity:.5;pointer-events:none}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='rgba(0, 0, 0, 0.25)'/%3E%3C/svg%3E");background-position:0;border-radius:2em;margin-left:-2.5em;transition:background-position .15s ease-in-out;width:2em}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%2386b7fe'/%3E%3C/svg%3E")}.form-switch .form-check-input:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E");background-position:100%}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{clip:rect(0,0,0,0);pointer-events:none;position:absolute}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{-webkit-filter:none;filter:none;opacity:.65;pointer-events:none}.form-range{-webkit-appearance:none;appearance:none;background-color:transparent;height:1.5rem;padding:0;width:100%}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;height:1rem;margin-top:-.25rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;width:1rem}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{background-color:#dee2e6;border-color:transparent;border-radius:1rem;color:transparent;cursor:pointer;height:.5rem;width:100%}.form-range::-moz-range-thumb{appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;height:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;width:1rem}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{background-color:#dee2e6;border-color:transparent;border-radius:1rem;color:transparent;cursor:pointer;height:.5rem;width:100%}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px);line-height:1.25}.form-floating>label{border:1px solid transparent;height:100%;left:0;padding:1rem .75rem;pointer-events:none;position:absolute;top:0;-webkit-transform-origin:0 0;transform-origin:0 0;transition:opacity .1s ease-in-out,-webkit-transform .1s ease-in-out;transition:opacity .1s ease-in-out,transform .1s ease-in-out;transition:opacity .1s ease-in-out,transform .1s ease-in-out,-webkit-transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control{padding:1rem .75rem}.form-floating>.form-control::-webkit-input-placeholder{color:transparent}.form-floating>.form-control:-ms-input-placeholder{color:transparent}.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control:not(:-ms-input-placeholder){padding-bottom:.625rem;padding-top:1.625rem}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-bottom:.625rem;padding-top:1.625rem}.form-floating>.form-control:-webkit-autofill{padding-bottom:.625rem;padding-top:1.625rem}.form-floating>.form-select{padding-bottom:.625rem;padding-top:1.625rem}.form-floating>.form-control:not(:-ms-input-placeholder)~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;-webkit-transform:scale(.85) translateY(-.5rem) translateX(.15rem);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;-webkit-transform:scale(.85) translateY(-.5rem) translateX(.15rem);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.input-group{align-items:stretch;display:flex;flex-wrap:wrap;position:relative;width:100%}.input-group>.form-control,.input-group>.form-select{flex:1 1 auto;min-width:0;position:relative;width:1%}.input-group>.form-control:focus,.input-group>.form-select:focus{z-index:3}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:3}.input-group-text{align-items:center;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem;color:#212529;display:flex;font-size:1rem;font-weight:400;line-height:1.5;padding:.375rem .75rem;text-align:center;white-space:nowrap}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{border-radius:.3rem;font-size:1.25rem;padding:.5rem 1rem}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{border-radius:.2rem;font-size:.875rem;padding:.25rem .5rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu),.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu){border-bottom-right-radius:0;border-top-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){border-bottom-left-radius:0;border-top-left-radius:0;margin-left:-1px}.valid-feedback{color:#198754;display:none;font-size:.875em;margin-top:.25rem;width:100%}.valid-tooltip{background-color:rgba(25,135,84,.9);border-radius:.25rem;color:#fff;display:none;font-size:.875rem;margin-top:.1rem;max-width:100%;padding:.25rem .5rem;position:absolute;top:100%;z-index:5}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E");background-position:right calc(.375em + .1875rem) center;background-repeat:no-repeat;background-size:calc(.75em + .375rem) calc(.75em + .375rem);border-color:#198754;padding-right:calc(1.5em + .75rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem);padding-right:calc(1.5em + .75rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:#198754}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3E%3C/svg%3E"),url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3E%3C/svg%3E");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem);padding-right:4.125rem}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:#198754}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:#198754}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#198754}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group .form-control.is-valid,.input-group .form-select.is-valid,.was-validated .input-group .form-control:valid,.was-validated .input-group .form-select:valid{z-index:1}.input-group .form-control.is-valid:focus,.input-group .form-select.is-valid:focus,.was-validated .input-group .form-control:valid:focus,.was-validated .input-group .form-select:valid:focus{z-index:3}.invalid-feedback{color:#dc3545;display:none;font-size:.875em;margin-top:.25rem;width:100%}.invalid-tooltip{background-color:rgba(220,53,69,.9);border-radius:.25rem;color:#fff;display:none;font-size:.875rem;margin-top:.1rem;max-width:100%;padding:.25rem .5rem;position:absolute;top:100%;z-index:5}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545'%3E%3Ccircle cx='6' cy='6' r='4.5'/%3E%3Cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3E%3Ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3E%3C/svg%3E");background-position:right calc(.375em + .1875rem) center;background-repeat:no-repeat;background-size:calc(.75em + .375rem) calc(.75em + .375rem);border-color:#dc3545;padding-right:calc(1.5em + .75rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem);padding-right:calc(1.5em + .75rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:#dc3545}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3E%3C/svg%3E"),url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545'%3E%3Ccircle cx='6' cy='6' r='4.5'/%3E%3Cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3E%3Ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3E%3C/svg%3E");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem);padding-right:4.125rem}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:#dc3545}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:#dc3545}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group .form-control.is-invalid,.input-group .form-select.is-invalid,.was-validated .input-group .form-control:invalid,.was-validated .input-group .form-select:invalid{z-index:2}.input-group .form-control.is-invalid:focus,.input-group .form-select.is-invalid:focus,.was-validated .input-group .form-control:invalid:focus,.was-validated .input-group .form-select:invalid:focus{z-index:3}.btn{background-color:transparent;border:1px solid transparent;border-radius:.25rem;color:#212529;cursor:pointer;display:inline-block;font-size:1rem;font-weight:400;line-height:1.5;padding:.375rem .75rem;text-align:center;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-user-select:none;-ms-user-select:none;user-select:none;vertical-align:middle}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529}.btn-check:focus+.btn,.btn:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.25);outline:0}.btn.disabled,.btn:disabled,fieldset:disabled .btn{opacity:.65;pointer-events:none}.btn-primary{background-color:#0d6efd;border-color:#0d6efd;color:#fff}.btn-check:focus+.btn-primary,.btn-primary:focus,.btn-primary:hover{background-color:#0b5ed7;border-color:#0a58ca;color:#fff}.btn-check:focus+.btn-primary,.btn-primary:focus{box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-check:active+.btn-primary,.btn-check:checked+.btn-primary,.btn-primary.active,.btn-primary:active,.show>.btn-primary.dropdown-toggle{background-color:#0a58ca;border-color:#0a53be;color:#fff}.btn-check:active+.btn-primary:focus,.btn-check:checked+.btn-primary:focus,.btn-primary.active:focus,.btn-primary:active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-primary.disabled,.btn-primary:disabled{background-color:#0d6efd;border-color:#0d6efd;color:#fff}.btn-secondary{background-color:#6c757d;border-color:#6c757d;color:#fff}.btn-check:focus+.btn-secondary,.btn-secondary:focus,.btn-secondary:hover{background-color:#5c636a;border-color:#565e64;color:#fff}.btn-check:focus+.btn-secondary,.btn-secondary:focus{box-shadow:0 0 0 .25rem hsla(208,6%,54%,.5)}.btn-check:active+.btn-secondary,.btn-check:checked+.btn-secondary,.btn-secondary.active,.btn-secondary:active,.show>.btn-secondary.dropdown-toggle{background-color:#565e64;border-color:#51585e;color:#fff}.btn-check:active+.btn-secondary:focus,.btn-check:checked+.btn-secondary:focus,.btn-secondary.active:focus,.btn-secondary:active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem hsla(208,6%,54%,.5)}.btn-secondary.disabled,.btn-secondary:disabled{background-color:#6c757d;border-color:#6c757d;color:#fff}.btn-success{background-color:#198754;border-color:#198754;color:#fff}.btn-check:focus+.btn-success,.btn-success:focus,.btn-success:hover{background-color:#157347;border-color:#146c43;color:#fff}.btn-check:focus+.btn-success,.btn-success:focus{box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-check:active+.btn-success,.btn-check:checked+.btn-success,.btn-success.active,.btn-success:active,.show>.btn-success.dropdown-toggle{background-color:#146c43;border-color:#13653f;color:#fff}.btn-check:active+.btn-success:focus,.btn-check:checked+.btn-success:focus,.btn-success.active:focus,.btn-success:active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-success.disabled,.btn-success:disabled{background-color:#198754;border-color:#198754;color:#fff}.btn-info{background-color:#0dcaf0;border-color:#0dcaf0;color:#000}.btn-check:focus+.btn-info,.btn-info:focus,.btn-info:hover{background-color:#31d2f2;border-color:#25cff2;color:#000}.btn-check:focus+.btn-info,.btn-info:focus{box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-check:active+.btn-info,.btn-check:checked+.btn-info,.btn-info.active,.btn-info:active,.show>.btn-info.dropdown-toggle{background-color:#3dd5f3;border-color:#25cff2;color:#000}.btn-check:active+.btn-info:focus,.btn-check:checked+.btn-info:focus,.btn-info.active:focus,.btn-info:active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-info.disabled,.btn-info:disabled{background-color:#0dcaf0;border-color:#0dcaf0;color:#000}.btn-warning{background-color:#ffc107;border-color:#ffc107;color:#000}.btn-check:focus+.btn-warning,.btn-warning:focus,.btn-warning:hover{background-color:#ffca2c;border-color:#ffc720;color:#000}.btn-check:focus+.btn-warning,.btn-warning:focus{box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-check:active+.btn-warning,.btn-check:checked+.btn-warning,.btn-warning.active,.btn-warning:active,.show>.btn-warning.dropdown-toggle{background-color:#ffcd39;border-color:#ffc720;color:#000}.btn-check:active+.btn-warning:focus,.btn-check:checked+.btn-warning:focus,.btn-warning.active:focus,.btn-warning:active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-warning.disabled,.btn-warning:disabled{background-color:#ffc107;border-color:#ffc107;color:#000}.btn-danger{background-color:#dc3545;border-color:#dc3545;color:#fff}.btn-check:focus+.btn-danger,.btn-danger:focus,.btn-danger:hover{background-color:#bb2d3b;border-color:#b02a37;color:#fff}.btn-check:focus+.btn-danger,.btn-danger:focus{box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-check:active+.btn-danger,.btn-check:checked+.btn-danger,.btn-danger.active,.btn-danger:active,.show>.btn-danger.dropdown-toggle{background-color:#b02a37;border-color:#a52834;color:#fff}.btn-check:active+.btn-danger:focus,.btn-check:checked+.btn-danger:focus,.btn-danger.active:focus,.btn-danger:active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{background-color:#dc3545;border-color:#dc3545;color:#fff}.btn-light{background-color:#f8f9fa;border-color:#f8f9fa;color:#000}.btn-check:focus+.btn-light,.btn-light:focus,.btn-light:hover{background-color:#f9fafb;border-color:#f9fafb;color:#000}.btn-check:focus+.btn-light,.btn-light:focus{box-shadow:0 0 0 .25rem hsla(210,2%,83%,.5)}.btn-check:active+.btn-light,.btn-check:checked+.btn-light,.btn-light.active,.btn-light:active,.show>.btn-light.dropdown-toggle{background-color:#f9fafb;border-color:#f9fafb;color:#000}.btn-check:active+.btn-light:focus,.btn-check:checked+.btn-light:focus,.btn-light.active:focus,.btn-light:active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .25rem hsla(210,2%,83%,.5)}.btn-light.disabled,.btn-light:disabled{background-color:#f8f9fa;border-color:#f8f9fa;color:#000}.btn-dark{background-color:#212529;border-color:#212529;color:#fff}.btn-check:focus+.btn-dark,.btn-dark:focus,.btn-dark:hover{background-color:#1c1f23;border-color:#1a1e21;color:#fff}.btn-check:focus+.btn-dark,.btn-dark:focus{box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-check:active+.btn-dark,.btn-check:checked+.btn-dark,.btn-dark.active,.btn-dark:active,.show>.btn-dark.dropdown-toggle{background-color:#1a1e21;border-color:#191c1f;color:#fff}.btn-check:active+.btn-dark:focus,.btn-check:checked+.btn-dark:focus,.btn-dark.active:focus,.btn-dark:active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-dark.disabled,.btn-dark:disabled{background-color:#212529;border-color:#212529;color:#fff}.btn-outline-primary{border-color:#0d6efd;color:#0d6efd}.btn-outline-primary:hover{background-color:#0d6efd;border-color:#0d6efd;color:#fff}.btn-check:focus+.btn-outline-primary,.btn-outline-primary:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-check:active+.btn-outline-primary,.btn-check:checked+.btn-outline-primary,.btn-outline-primary.active,.btn-outline-primary.dropdown-toggle.show,.btn-outline-primary:active{background-color:#0d6efd;border-color:#0d6efd;color:#fff}.btn-check:active+.btn-outline-primary:focus,.btn-check:checked+.btn-outline-primary:focus,.btn-outline-primary.active:focus,.btn-outline-primary.dropdown-toggle.show:focus,.btn-outline-primary:active:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{background-color:transparent;color:#0d6efd}.btn-outline-secondary{border-color:#6c757d;color:#6c757d}.btn-outline-secondary:hover{background-color:#6c757d;border-color:#6c757d;color:#fff}.btn-check:focus+.btn-outline-secondary,.btn-outline-secondary:focus{box-shadow:0 0 0 .25rem hsla(208,7%,46%,.5)}.btn-check:active+.btn-outline-secondary,.btn-check:checked+.btn-outline-secondary,.btn-outline-secondary.active,.btn-outline-secondary.dropdown-toggle.show,.btn-outline-secondary:active{background-color:#6c757d;border-color:#6c757d;color:#fff}.btn-check:active+.btn-outline-secondary:focus,.btn-check:checked+.btn-outline-secondary:focus,.btn-outline-secondary.active:focus,.btn-outline-secondary.dropdown-toggle.show:focus,.btn-outline-secondary:active:focus{box-shadow:0 0 0 .25rem hsla(208,7%,46%,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{background-color:transparent;color:#6c757d}.btn-outline-success{border-color:#198754;color:#198754}.btn-outline-success:hover{background-color:#198754;border-color:#198754;color:#fff}.btn-check:focus+.btn-outline-success,.btn-outline-success:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-check:active+.btn-outline-success,.btn-check:checked+.btn-outline-success,.btn-outline-success.active,.btn-outline-success.dropdown-toggle.show,.btn-outline-success:active{background-color:#198754;border-color:#198754;color:#fff}.btn-check:active+.btn-outline-success:focus,.btn-check:checked+.btn-outline-success:focus,.btn-outline-success.active:focus,.btn-outline-success.dropdown-toggle.show:focus,.btn-outline-success:active:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{background-color:transparent;color:#198754}.btn-outline-info{border-color:#0dcaf0;color:#0dcaf0}.btn-outline-info:hover{background-color:#0dcaf0;border-color:#0dcaf0;color:#000}.btn-check:focus+.btn-outline-info,.btn-outline-info:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-check:active+.btn-outline-info,.btn-check:checked+.btn-outline-info,.btn-outline-info.active,.btn-outline-info.dropdown-toggle.show,.btn-outline-info:active{background-color:#0dcaf0;border-color:#0dcaf0;color:#000}.btn-check:active+.btn-outline-info:focus,.btn-check:checked+.btn-outline-info:focus,.btn-outline-info.active:focus,.btn-outline-info.dropdown-toggle.show:focus,.btn-outline-info:active:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{background-color:transparent;color:#0dcaf0}.btn-outline-warning{border-color:#ffc107;color:#ffc107}.btn-outline-warning:hover{background-color:#ffc107;border-color:#ffc107;color:#000}.btn-check:focus+.btn-outline-warning,.btn-outline-warning:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-check:active+.btn-outline-warning,.btn-check:checked+.btn-outline-warning,.btn-outline-warning.active,.btn-outline-warning.dropdown-toggle.show,.btn-outline-warning:active{background-color:#ffc107;border-color:#ffc107;color:#000}.btn-check:active+.btn-outline-warning:focus,.btn-check:checked+.btn-outline-warning:focus,.btn-outline-warning.active:focus,.btn-outline-warning.dropdown-toggle.show:focus,.btn-outline-warning:active:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{background-color:transparent;color:#ffc107}.btn-outline-danger{border-color:#dc3545;color:#dc3545}.btn-outline-danger:hover{background-color:#dc3545;border-color:#dc3545;color:#fff}.btn-check:focus+.btn-outline-danger,.btn-outline-danger:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-check:active+.btn-outline-danger,.btn-check:checked+.btn-outline-danger,.btn-outline-danger.active,.btn-outline-danger.dropdown-toggle.show,.btn-outline-danger:active{background-color:#dc3545;border-color:#dc3545;color:#fff}.btn-check:active+.btn-outline-danger:focus,.btn-check:checked+.btn-outline-danger:focus,.btn-outline-danger.active:focus,.btn-outline-danger.dropdown-toggle.show:focus,.btn-outline-danger:active:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{background-color:transparent;color:#dc3545}.btn-outline-light{border-color:#f8f9fa;color:#f8f9fa}.btn-outline-light:hover{background-color:#f8f9fa;border-color:#f8f9fa;color:#000}.btn-check:focus+.btn-outline-light,.btn-outline-light:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-check:active+.btn-outline-light,.btn-check:checked+.btn-outline-light,.btn-outline-light.active,.btn-outline-light.dropdown-toggle.show,.btn-outline-light:active{background-color:#f8f9fa;border-color:#f8f9fa;color:#000}.btn-check:active+.btn-outline-light:focus,.btn-check:checked+.btn-outline-light:focus,.btn-outline-light.active:focus,.btn-outline-light.dropdown-toggle.show:focus,.btn-outline-light:active:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{background-color:transparent;color:#f8f9fa}.btn-outline-dark{border-color:#212529;color:#212529}.btn-outline-dark:hover{background-color:#212529;border-color:#212529;color:#fff}.btn-check:focus+.btn-outline-dark,.btn-outline-dark:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-check:active+.btn-outline-dark,.btn-check:checked+.btn-outline-dark,.btn-outline-dark.active,.btn-outline-dark.dropdown-toggle.show,.btn-outline-dark:active{background-color:#212529;border-color:#212529;color:#fff}.btn-check:active+.btn-outline-dark:focus,.btn-check:checked+.btn-outline-dark:focus,.btn-outline-dark.active:focus,.btn-outline-dark.dropdown-toggle.show:focus,.btn-outline-dark:active:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{background-color:transparent;color:#212529}.btn-link{color:#0d6efd;font-weight:400;text-decoration:underline}.btn-link:hover{color:#0a58ca}.btn-link.disabled,.btn-link:disabled{color:#6c757d}.btn-group-lg>.btn,.btn-lg{border-radius:.3rem;font-size:1.25rem;padding:.5rem 1rem}.btn-group-sm>.btn,.btn-sm{border-radius:.2rem;font-size:.875rem;padding:.25rem .5rem}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{height:auto;transition:width .35s ease;width:0}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropend,.dropstart,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle:after{border-bottom:0;border-left:.3em solid transparent;border-right:.3em solid transparent;border-top:.3em solid;content:"";display:inline-block;margin-left:.255em;vertical-align:.255em}.dropdown-toggle:empty:after{margin-left:0}.dropdown-menu{background-clip:padding-box;background-color:#fff;border:1px solid rgba(0,0,0,.15);border-radius:.25rem;color:#212529;display:none;font-size:1rem;list-style:none;margin:0;min-width:10rem;padding:.5rem 0;position:absolute;text-align:left;z-index:1000}.dropdown-menu[data-bs-popper]{left:0;margin-top:.125rem;top:100%}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{left:0;right:auto}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{left:auto;right:0}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{left:0;right:auto}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{left:auto;right:0}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{left:0;right:auto}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{left:auto;right:0}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{left:0;right:auto}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{left:auto;right:0}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{left:0;right:auto}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{left:auto;right:0}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{left:0;right:auto}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{left:auto;right:0}}.dropup .dropdown-menu[data-bs-popper]{bottom:100%;margin-bottom:.125rem;margin-top:0;top:auto}.dropup .dropdown-toggle:after{border-bottom:.3em solid;border-left:.3em solid transparent;border-right:.3em solid transparent;border-top:0;content:"";display:inline-block;margin-left:.255em;vertical-align:.255em}.dropup .dropdown-toggle:empty:after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{left:100%;margin-left:.125rem;margin-top:0;right:auto;top:0}.dropend .dropdown-toggle:after{border-bottom:.3em solid transparent;border-left:.3em solid;border-right:0;border-top:.3em solid transparent;content:"";display:inline-block;margin-left:.255em;vertical-align:.255em}.dropend .dropdown-toggle:empty:after{margin-left:0}.dropend .dropdown-toggle:after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{left:auto;margin-right:.125rem;margin-top:0;right:100%;top:0}.dropstart .dropdown-toggle:after{content:"";display:inline-block;display:none;margin-left:.255em;vertical-align:.255em}.dropstart .dropdown-toggle:before{border-bottom:.3em solid transparent;border-right:.3em solid;border-top:.3em solid transparent;content:"";display:inline-block;margin-right:.255em;vertical-align:.255em}.dropstart .dropdown-toggle:empty:after{margin-left:0}.dropstart .dropdown-toggle:before{vertical-align:0}.dropdown-divider{border-top:1px solid rgba(0,0,0,.15);height:0;margin:.5rem 0;overflow:hidden}.dropdown-item{background-color:transparent;border:0;clear:both;color:#212529;display:block;font-weight:400;padding:.25rem 1rem;text-align:inherit;text-decoration:none;white-space:nowrap;width:100%}.dropdown-item:focus,.dropdown-item:hover{background-color:#e9ecef;color:#1e2125}.dropdown-item.active,.dropdown-item:active{background-color:#0d6efd;color:#fff;text-decoration:none}.dropdown-item.disabled,.dropdown-item:disabled{background-color:transparent;color:#adb5bd;pointer-events:none}.dropdown-menu.show{display:block}.dropdown-header{color:#6c757d;display:block;font-size:.875rem;margin-bottom:0;padding:.5rem 1rem;white-space:nowrap}.dropdown-item-text{color:#212529;display:block;padding:.25rem 1rem}.dropdown-menu-dark{background-color:#343a40;border-color:rgba(0,0,0,.15);color:#dee2e6}.dropdown-menu-dark .dropdown-item{color:#dee2e6}.dropdown-menu-dark .dropdown-item:focus,.dropdown-menu-dark .dropdown-item:hover{background-color:hsla(0,0%,100%,.15);color:#fff}.dropdown-menu-dark .dropdown-item.active,.dropdown-menu-dark .dropdown-item:active{background-color:#0d6efd;color:#fff}.dropdown-menu-dark .dropdown-item.disabled,.dropdown-menu-dark .dropdown-item:disabled{color:#adb5bd}.dropdown-menu-dark .dropdown-divider{border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item-text{color:#dee2e6}.dropdown-menu-dark .dropdown-header{color:#adb5bd}.btn-group,.btn-group-vertical{display:inline-flex;position:relative;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{flex:1 1 auto;position:relative}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-bottom-left-radius:0;border-top-left-radius:0}.dropdown-toggle-split{padding-left:.5625rem;padding-right:.5625rem}.dropdown-toggle-split:after,.dropend .dropdown-toggle-split:after,.dropup .dropdown-toggle-split:after{margin-left:0}.dropstart .dropdown-toggle-split:before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-left:.375rem;padding-right:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-left:.75rem;padding-right:.75rem}.btn-group-vertical{align-items:flex-start;flex-direction:column;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-left-radius:0;border-bottom-right-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{display:flex;flex-wrap:wrap;list-style:none;margin-bottom:0;padding-left:0}.nav-link{color:#0d6efd;display:block;padding:.5rem 1rem;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:#0a58ca}.nav-link.disabled{color:#6c757d;cursor:default;pointer-events:none}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{background:0 0;border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem;margin-bottom:-1px}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6;isolation:isolate}.nav-tabs .nav-link.disabled{background-color:transparent;border-color:transparent;color:#6c757d}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{background-color:#fff;border-color:#dee2e6 #dee2e6 #fff;color:#495057}.nav-tabs .dropdown-menu{border-top-left-radius:0;border-top-right-radius:0;margin-top:-1px}.nav-pills .nav-link{background:0 0;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{background-color:#0d6efd;color:#fff}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{align-items:center;display:flex;flex-wrap:wrap;justify-content:space-between;padding-bottom:.5rem;padding-top:.5rem;position:relative}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{align-items:center;display:flex;flex-wrap:inherit;justify-content:space-between}.navbar-brand{font-size:1.25rem;margin-right:1rem;padding-bottom:.3125rem;padding-top:.3125rem;text-decoration:none;white-space:nowrap}.navbar-nav{display:flex;flex-direction:column;list-style:none;margin-bottom:0;padding-left:0}.navbar-nav .nav-link{padding-left:0;padding-right:0}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-bottom:.5rem;padding-top:.5rem}.navbar-collapse{align-items:center;flex-basis:100%;flex-grow:1}.navbar-toggler{background-color:transparent;border:1px solid transparent;border-radius:.25rem;font-size:1.25rem;line-height:1;padding:.25rem .75rem;transition:box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{box-shadow:0 0 0 .25rem;outline:0;text-decoration:none}.navbar-toggler-icon{background-position:50%;background-repeat:no-repeat;background-size:100%;display:inline-block;height:1.5em;vertical-align:middle;width:1.5em}.navbar-nav-scroll{max-height:75vh;max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-left:.5rem;padding-right:.5rem}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler,.navbar-expand-sm .offcanvas-header{display:none}.navbar-expand-sm .offcanvas{background-color:transparent;border-left:0;border-right:0;bottom:0;flex-grow:1;position:inherit;-webkit-transform:none;transform:none;transition:none;visibility:visible!important;z-index:1000}.navbar-expand-sm .offcanvas-bottom,.navbar-expand-sm .offcanvas-top{border-bottom:0;border-top:0;height:auto}.navbar-expand-sm .offcanvas-body{display:flex;flex-grow:0;overflow-y:visible;padding:0}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-left:.5rem;padding-right:.5rem}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler,.navbar-expand-md .offcanvas-header{display:none}.navbar-expand-md .offcanvas{background-color:transparent;border-left:0;border-right:0;bottom:0;flex-grow:1;position:inherit;-webkit-transform:none;transform:none;transition:none;visibility:visible!important;z-index:1000}.navbar-expand-md .offcanvas-bottom,.navbar-expand-md .offcanvas-top{border-bottom:0;border-top:0;height:auto}.navbar-expand-md .offcanvas-body{display:flex;flex-grow:0;overflow-y:visible;padding:0}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-left:.5rem;padding-right:.5rem}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler,.navbar-expand-lg .offcanvas-header{display:none}.navbar-expand-lg .offcanvas{background-color:transparent;border-left:0;border-right:0;bottom:0;flex-grow:1;position:inherit;-webkit-transform:none;transform:none;transition:none;visibility:visible!important;z-index:1000}.navbar-expand-lg .offcanvas-bottom,.navbar-expand-lg .offcanvas-top{border-bottom:0;border-top:0;height:auto}.navbar-expand-lg .offcanvas-body{display:flex;flex-grow:0;overflow-y:visible;padding:0}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-left:.5rem;padding-right:.5rem}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler,.navbar-expand-xl .offcanvas-header{display:none}.navbar-expand-xl .offcanvas{background-color:transparent;border-left:0;border-right:0;bottom:0;flex-grow:1;position:inherit;-webkit-transform:none;transform:none;transition:none;visibility:visible!important;z-index:1000}.navbar-expand-xl .offcanvas-bottom,.navbar-expand-xl .offcanvas-top{border-bottom:0;border-top:0;height:auto}.navbar-expand-xl .offcanvas-body{display:flex;flex-grow:0;overflow-y:visible;padding:0}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-left:.5rem;padding-right:.5rem}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler,.navbar-expand-xxl .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas{background-color:transparent;border-left:0;border-right:0;bottom:0;flex-grow:1;position:inherit;-webkit-transform:none;transform:none;transition:none;visibility:visible!important;z-index:1000}.navbar-expand-xxl .offcanvas-bottom,.navbar-expand-xxl .offcanvas-top{border-bottom:0;border-top:0;height:auto}.navbar-expand-xxl .offcanvas-body{display:flex;flex-grow:0;overflow-y:visible;padding:0}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-left:.5rem;padding-right:.5rem}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler,.navbar-expand .offcanvas-header{display:none}.navbar-expand .offcanvas{background-color:transparent;border-left:0;border-right:0;bottom:0;flex-grow:1;position:inherit;-webkit-transform:none;transform:none;transition:none;visibility:visible!important;z-index:1000}.navbar-expand .offcanvas-bottom,.navbar-expand .offcanvas-top{border-bottom:0;border-top:0;height:auto}.navbar-expand .offcanvas-body{display:flex;flex-grow:0;overflow-y:visible;padding:0}.navbar-light .navbar-brand,.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.55)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{border-color:rgba(0,0,0,.1);color:rgba(0,0,0,.55)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3E%3Cpath stroke='rgba(0, 0, 0, 0.55)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.55)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand,.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:hsla(0,0%,100%,.55)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:hsla(0,0%,100%,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:hsla(0,0%,100%,.25)}.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{border-color:hsla(0,0%,100%,.1);color:hsla(0,0%,100%,.55)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3E%3Cpath stroke='rgba(255, 255, 255, 0.55)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:hsla(0,0%,100%,.55)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{word-wrap:break-word;background-clip:border-box;background-color:#fff;border:1px solid rgba(0,0,0,.125);border-radius:.25rem;display:flex;flex-direction:column;min-width:0;position:relative}.card>hr{margin-left:0;margin-right:0}.card>.list-group{border-bottom:inherit;border-top:inherit}.card>.list-group:first-child{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px);border-top-width:0}.card>.list-group:last-child{border-bottom-left-radius:calc(.25rem - 1px);border-bottom-right-radius:calc(.25rem - 1px);border-bottom-width:0}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:1rem}.card-title{margin-bottom:.5rem}.card-subtitle{margin-top:-.25rem}.card-subtitle,.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:1rem}.card-header{background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125);margin-bottom:0;padding:.5rem 1rem}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125);padding:.5rem 1rem}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{border-bottom:0;margin-bottom:-.5rem}.card-header-pills,.card-header-tabs{margin-left:-.5rem;margin-right:-.5rem}.card-img-overlay{border-radius:calc(.25rem - 1px);bottom:0;left:0;padding:1rem;position:absolute;right:0;top:0}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-left-radius:calc(.25rem - 1px);border-bottom-right-radius:calc(.25rem - 1px)}.card-group>.card{margin-bottom:.75rem}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0;margin-bottom:0}.card-group>.card+.card{border-left:0;margin-left:0}.card-group>.card:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion-button{align-items:center;background-color:#fff;border:0;border-radius:0;color:#212529;display:flex;font-size:1rem;overflow-anchor:none;padding:1rem 1.25rem;position:relative;text-align:left;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease;width:100%}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){background-color:#e7f1ff;box-shadow:inset 0 -1px 0 rgba(0,0,0,.125);color:#0c63e4}.accordion-button:not(.collapsed):after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3E%3Cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E");-webkit-transform:rotate(-180deg);transform:rotate(-180deg)}.accordion-button:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3E%3Cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-size:1.25rem;content:"";flex-shrink:0;height:1.25rem;margin-left:auto;transition:-webkit-transform .2s ease-in-out;transition:transform .2s ease-in-out;transition:transform .2s ease-in-out,-webkit-transform .2s ease-in-out;width:1.25rem}@media (prefers-reduced-motion:reduce){.accordion-button:after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{border-color:#86b7fe;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);outline:0;z-index:3}.accordion-header{margin-bottom:0}.accordion-item{background-color:#fff;border:1px solid rgba(0,0,0,.125)}.accordion-item:first-of-type{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.accordion-item:first-of-type .accordion-button{border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-left-radius:calc(.25rem - 1px);border-bottom-right-radius:calc(.25rem - 1px)}.accordion-item:last-of-type .accordion-collapse{border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem}.accordion-body{padding:1rem 1.25rem}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-left:0;border-radius:0;border-right:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button{border-radius:0}.breadcrumb{display:flex;flex-wrap:wrap;list-style:none;margin-bottom:1rem;padding:0}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item:before{color:#6c757d;content:"/";content:var(--bs-breadcrumb-divider,"/");float:left;padding-right:.5rem}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;list-style:none;padding-left:0}.page-link{background-color:#fff;border:1px solid #dee2e6;color:#0d6efd;display:block;position:relative;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{border-color:#dee2e6;z-index:2}.page-link:focus,.page-link:hover{background-color:#e9ecef;color:#0a58ca}.page-link:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.25);outline:0;z-index:3}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item.active .page-link{background-color:#0d6efd;border-color:#0d6efd;color:#fff;z-index:3}.page-item.disabled .page-link{background-color:#fff;border-color:#dee2e6;color:#6c757d;pointer-events:none}.page-link{padding:.375rem .75rem}.page-item:first-child .page-link{border-bottom-left-radius:.25rem;border-top-left-radius:.25rem}.page-item:last-child .page-link{border-bottom-right-radius:.25rem;border-top-right-radius:.25rem}.pagination-lg .page-link{font-size:1.25rem;padding:.75rem 1.5rem}.pagination-lg .page-item:first-child .page-link{border-bottom-left-radius:.3rem;border-top-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-bottom-right-radius:.3rem;border-top-right-radius:.3rem}.pagination-sm .page-link{font-size:.875rem;padding:.25rem .5rem}.pagination-sm .page-item:first-child .page-link{border-bottom-left-radius:.2rem;border-top-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-bottom-right-radius:.2rem;border-top-right-radius:.2rem}.badge{border-radius:.25rem;color:#fff;display:inline-block;font-size:.75em;font-weight:700;line-height:1;padding:.35em .65em;text-align:center;vertical-align:baseline;white-space:nowrap}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{border:1px solid transparent;border-radius:.25rem;margin-bottom:1rem;padding:1rem;position:relative}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{padding:1.25rem 1rem;position:absolute;right:0;top:0;z-index:2}.alert-primary{background-color:#cfe2ff;border-color:#b6d4fe;color:#084298}.alert-primary .alert-link{color:#06357a}.alert-secondary{background-color:#e2e3e5;border-color:#d3d6d8;color:#41464b}.alert-secondary .alert-link{color:#34383c}.alert-success{background-color:#d1e7dd;border-color:#badbcc;color:#0f5132}.alert-success .alert-link{color:#0c4128}.alert-info{background-color:#cff4fc;border-color:#b6effb;color:#055160}.alert-info .alert-link{color:#04414d}.alert-warning{background-color:#fff3cd;border-color:#ffecb5;color:#664d03}.alert-warning .alert-link{color:#523e02}.alert-danger{background-color:#f8d7da;border-color:#f5c2c7;color:#842029}.alert-danger .alert-link{color:#6a1a21}.alert-light{background-color:#fefefe;border-color:#fdfdfe;color:#636464}.alert-light .alert-link{color:#4f5050}.alert-dark{background-color:#d3d3d4;border-color:#bcbebf;color:#141619}.alert-dark .alert-link{color:#101214}@-webkit-keyframes progress-bar-stripes{0%{background-position-x:1rem}}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{background-color:#e9ecef;border-radius:.25rem;font-size:.75rem;height:1rem}.progress,.progress-bar{display:flex;overflow:hidden}.progress-bar{background-color:#0d6efd;color:#fff;flex-direction:column;justify-content:center;text-align:center;transition:width .6s ease;white-space:nowrap}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 0,transparent 50%,hsla(0,0%,100%,.15) 0,hsla(0,0%,100%,.15) 75%,transparent 0,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.list-group{border-radius:.25rem;display:flex;flex-direction:column;margin-bottom:0;padding-left:0}.list-group-numbered{counter-reset:section;list-style-type:none}.list-group-numbered>li:before{content:counters(section,".") ". ";counter-increment:section}.list-group-item-action{color:#495057;text-align:inherit;width:100%}.list-group-item-action:focus,.list-group-item-action:hover{background-color:#f8f9fa;color:#495057;text-decoration:none;z-index:1}.list-group-item-action:active{background-color:#e9ecef;color:#212529}.list-group-item{background-color:#fff;border:1px solid rgba(0,0,0,.125);color:#212529;display:block;padding:.5rem 1rem;position:relative;text-decoration:none}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{background-color:#fff;color:#6c757d;pointer-events:none}.list-group-item.active{background-color:#0d6efd;border-color:#0d6efd;color:#fff;z-index:2}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{border-top-width:1px;margin-top:-1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child{border-bottom-left-radius:0;border-top-right-radius:.25rem}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-left-width:0;border-top-width:1px}.list-group-horizontal>.list-group-item+.list-group-item.active{border-left-width:1px;margin-left:-1px}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-bottom-left-radius:0;border-top-right-radius:.25rem}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-left-width:0;border-top-width:1px}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{border-left-width:1px;margin-left:-1px}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-bottom-left-radius:0;border-top-right-radius:.25rem}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-left-width:0;border-top-width:1px}.list-group-horizontal-md>.list-group-item+.list-group-item.active{border-left-width:1px;margin-left:-1px}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-bottom-left-radius:0;border-top-right-radius:.25rem}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-left-width:0;border-top-width:1px}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{border-left-width:1px;margin-left:-1px}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-bottom-left-radius:0;border-top-right-radius:.25rem}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-left-width:0;border-top-width:1px}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{border-left-width:1px;margin-left:-1px}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child{border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child{border-bottom-left-radius:0;border-top-right-radius:.25rem}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-left-width:0;border-top-width:1px}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{border-left-width:1px;margin-left:-1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{background-color:#cfe2ff;color:#084298}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{background-color:#bacbe6;color:#084298}.list-group-item-primary.list-group-item-action.active{background-color:#084298;border-color:#084298;color:#fff}.list-group-item-secondary{background-color:#e2e3e5;color:#41464b}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{background-color:#cbccce;color:#41464b}.list-group-item-secondary.list-group-item-action.active{background-color:#41464b;border-color:#41464b;color:#fff}.list-group-item-success{background-color:#d1e7dd;color:#0f5132}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{background-color:#bcd0c7;color:#0f5132}.list-group-item-success.list-group-item-action.active{background-color:#0f5132;border-color:#0f5132;color:#fff}.list-group-item-info{background-color:#cff4fc;color:#055160}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{background-color:#badce3;color:#055160}.list-group-item-info.list-group-item-action.active{background-color:#055160;border-color:#055160;color:#fff}.list-group-item-warning{background-color:#fff3cd;color:#664d03}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{background-color:#e6dbb9;color:#664d03}.list-group-item-warning.list-group-item-action.active{background-color:#664d03;border-color:#664d03;color:#fff}.list-group-item-danger{background-color:#f8d7da;color:#842029}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{background-color:#dfc2c4;color:#842029}.list-group-item-danger.list-group-item-action.active{background-color:#842029;border-color:#842029;color:#fff}.list-group-item-light{background-color:#fefefe;color:#636464}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{background-color:#e5e5e5;color:#636464}.list-group-item-light.list-group-item-action.active{background-color:#636464;border-color:#636464;color:#fff}.list-group-item-dark{background-color:#d3d3d4;color:#141619}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{background-color:#bebebf;color:#141619}.list-group-item-dark.list-group-item-action.active{background-color:#141619;border-color:#141619;color:#fff}.btn-close{background:transparent url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3E%3C/svg%3E") 50%/1em auto no-repeat;border:0;border-radius:.25rem;box-sizing:content-box;color:#000;height:1em;opacity:.5;padding:.25em;width:1em}.btn-close:hover{color:#000;opacity:.75;text-decoration:none}.btn-close:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.25);opacity:1;outline:0}.btn-close.disabled,.btn-close:disabled{opacity:.25;pointer-events:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.btn-close-white{-webkit-filter:invert(1) grayscale(100%) brightness(200%);filter:invert(1) grayscale(100%) brightness(200%)}.toast{background-clip:padding-box;background-color:hsla(0,0%,100%,.85);border:1px solid rgba(0,0,0,.1);border-radius:.25rem;box-shadow:0 .5rem 1rem rgba(0,0,0,.15);font-size:.875rem;max-width:100%;pointer-events:auto;width:350px}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{max-width:100%;pointer-events:none;width:-webkit-max-content;width:max-content}.toast-container>:not(:last-child){margin-bottom:.75rem}.toast-header{align-items:center;background-clip:padding-box;background-color:hsla(0,0%,100%,.85);border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px);color:#6c757d;display:flex;padding:.5rem .75rem}.toast-header .btn-close{margin-left:.75rem;margin-right:-.375rem}.toast-body{word-wrap:break-word;padding:.75rem}.modal{display:none;height:100%;left:0;outline:0;overflow-x:hidden;overflow-y:auto;position:fixed;top:0;width:100%;z-index:1055}.modal-dialog{margin:.5rem;pointer-events:none;position:relative;width:auto}.modal.fade .modal-dialog{-webkit-transform:translateY(-50px);transform:translateY(-50px);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{align-items:center;display:flex;min-height:calc(100% - 1rem)}.modal-content{background-clip:padding-box;background-color:#fff;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;display:flex;flex-direction:column;outline:0;pointer-events:auto;position:relative;width:100%}.modal-backdrop{background-color:#000;height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:1050}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{align-items:center;border-bottom:1px solid #dee2e6;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px);display:flex;flex-shrink:0;justify-content:space-between;padding:1rem}.modal-header .btn-close{margin:-.5rem -.5rem -.5rem auto;padding:.5rem}.modal-title{line-height:1.5;margin-bottom:0}.modal-body{flex:1 1 auto;padding:1rem;position:relative}.modal-footer{align-items:center;border-bottom-left-radius:calc(.3rem - 1px);border-bottom-right-radius:calc(.3rem - 1px);border-top:1px solid #dee2e6;display:flex;flex-shrink:0;flex-wrap:wrap;justify-content:flex-end;padding:.75rem}.modal-footer>*{margin:.25rem}@media (min-width:576px){.modal-dialog{margin:1.75rem auto;max-width:500px}.modal-dialog-scrollable{height:calc(100% - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.modal-fullscreen{height:100%;margin:0;max-width:none;width:100vw}.modal-fullscreen .modal-content{border:0;border-radius:0;height:100%}.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}.modal-fullscreen .modal-footer{border-radius:0}@media (max-width:575.98px){.modal-fullscreen-sm-down{height:100%;margin:0;max-width:none;width:100vw}.modal-fullscreen-sm-down .modal-content{border:0;border-radius:0;height:100%}.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}.modal-fullscreen-sm-down .modal-footer{border-radius:0}}@media (max-width:767.98px){.modal-fullscreen-md-down{height:100%;margin:0;max-width:none;width:100vw}.modal-fullscreen-md-down .modal-content{border:0;border-radius:0;height:100%}.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}.modal-fullscreen-md-down .modal-footer{border-radius:0}}@media (max-width:991.98px){.modal-fullscreen-lg-down{height:100%;margin:0;max-width:none;width:100vw}.modal-fullscreen-lg-down .modal-content{border:0;border-radius:0;height:100%}.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}.modal-fullscreen-lg-down .modal-footer{border-radius:0}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{height:100%;margin:0;max-width:none;width:100vw}.modal-fullscreen-xl-down .modal-content{border:0;border-radius:0;height:100%}.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}.modal-fullscreen-xl-down .modal-footer{border-radius:0}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{height:100%;margin:0;max-width:none;width:100vw}.modal-fullscreen-xxl-down .modal-content{border:0;border-radius:0;height:100%}.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}.modal-fullscreen-xxl-down .modal-footer{border-radius:0}}.tooltip{word-wrap:break-word;display:block;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,Liberation Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-family:var(--bs-font-sans-serif);font-size:.875rem;font-style:normal;font-weight:400;letter-spacing:normal;line-break:auto;line-height:1.5;margin:0;opacity:0;position:absolute;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;z-index:1080}.tooltip.show{opacity:.9}.tooltip .tooltip-arrow{display:block;height:.4rem;position:absolute;width:.8rem}.tooltip .tooltip-arrow:before{border-color:transparent;border-style:solid;content:"";position:absolute}.bs-tooltip-auto[data-popper-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow:before,.bs-tooltip-top .tooltip-arrow:before{border-top-color:#000;border-width:.4rem .4rem 0;top:-1px}.bs-tooltip-auto[data-popper-placement^=right],.bs-tooltip-end{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{height:.8rem;left:0;width:.4rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow:before,.bs-tooltip-end .tooltip-arrow:before{border-right-color:#000;border-width:.4rem .4rem .4rem 0;right:-1px}.bs-tooltip-auto[data-popper-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow:before,.bs-tooltip-bottom .tooltip-arrow:before{border-bottom-color:#000;border-width:0 .4rem .4rem;bottom:-1px}.bs-tooltip-auto[data-popper-placement^=left],.bs-tooltip-start{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{height:.8rem;right:0;width:.4rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow:before,.bs-tooltip-start .tooltip-arrow:before{border-left-color:#000;border-width:.4rem 0 .4rem .4rem;left:-1px}.tooltip-inner{background-color:#000;border-radius:.25rem;color:#fff;max-width:200px;padding:.25rem .5rem;text-align:center}.popover{word-wrap:break-word;background-clip:padding-box;background-color:#fff;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;display:block;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,Liberation Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-family:var(--bs-font-sans-serif);font-size:.875rem;font-style:normal;font-weight:400;left:0;letter-spacing:normal;line-break:auto;line-height:1.5;max-width:276px;position:absolute;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;top:0;white-space:normal;word-break:normal;word-spacing:normal;z-index:1070}.popover .popover-arrow{display:block;height:.5rem;position:absolute;width:1rem}.popover .popover-arrow:after,.popover .popover-arrow:before{border-color:transparent;border-style:solid;content:"";display:block;position:absolute}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow:before,.bs-popover-top>.popover-arrow:before{border-top-color:rgba(0,0,0,.25);border-width:.5rem .5rem 0;bottom:0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow:after,.bs-popover-top>.popover-arrow:after{border-top-color:#fff;border-width:.5rem .5rem 0;bottom:1px}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{height:1rem;left:calc(-.5rem - 1px);width:.5rem}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow:before,.bs-popover-end>.popover-arrow:before{border-right-color:rgba(0,0,0,.25);border-width:.5rem .5rem .5rem 0;left:0}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow:after,.bs-popover-end>.popover-arrow:after{border-right-color:#fff;border-width:.5rem .5rem .5rem 0;left:1px}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow:before,.bs-popover-bottom>.popover-arrow:before{border-bottom-color:rgba(0,0,0,.25);border-width:0 .5rem .5rem;top:0}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow:after,.bs-popover-bottom>.popover-arrow:after{border-bottom-color:#fff;border-width:0 .5rem .5rem;top:1px}.bs-popover-auto[data-popper-placement^=bottom] .popover-header:before,.bs-popover-bottom .popover-header:before{border-bottom:1px solid #f0f0f0;content:"";display:block;left:50%;margin-left:-.5rem;position:absolute;top:0;width:1rem}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{height:1rem;right:calc(-.5rem - 1px);width:.5rem}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow:before,.bs-popover-start>.popover-arrow:before{border-left-color:rgba(0,0,0,.25);border-width:.5rem 0 .5rem .5rem;right:0}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow:after,.bs-popover-start>.popover-arrow:after{border-left-color:#fff;border-width:.5rem 0 .5rem .5rem;right:1px}.popover-header{background-color:#f0f0f0;border-bottom:1px solid rgba(0,0,0,.2);border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px);font-size:1rem;margin-bottom:0;padding:.5rem 1rem}.popover-header:empty{display:none}.popover-body{color:#212529;padding:1rem}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{overflow:hidden;position:relative;width:100%}.carousel-inner:after{clear:both;content:"";display:block}.carousel-item{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:none;float:left;margin-right:-100%;position:relative;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out;width:100%}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;-webkit-transform:none;transform:none;transition-property:opacity}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{opacity:1;z-index:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{opacity:0;transition:opacity 0s .6s;z-index:0}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{align-items:center;background:0 0;border:0;bottom:0;color:#fff;display:flex;justify-content:center;opacity:.5;padding:0;position:absolute;text-align:center;top:0;transition:opacity .15s ease;width:15%;z-index:1}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;opacity:.9;outline:0;text-decoration:none}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{background-position:50%;background-repeat:no-repeat;background-size:100% 100%;display:inline-block;height:2rem;width:2rem}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3E%3Cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3E%3Cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E")}.carousel-indicators{bottom:0;display:flex;justify-content:center;left:0;list-style:none;margin-bottom:1rem;margin-left:15%;margin-right:15%;padding:0;position:absolute;right:0;z-index:2}.carousel-indicators [data-bs-target]{background-clip:padding-box;background-color:#fff;border:0;border-bottom:10px solid transparent;border-top:10px solid transparent;box-sizing:content-box;cursor:pointer;flex:0 1 auto;height:3px;margin-left:3px;margin-right:3px;opacity:.5;padding:0;text-indent:-999px;transition:opacity .6s ease;width:30px}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{bottom:1.25rem;color:#fff;left:15%;padding-bottom:1.25rem;padding-top:1.25rem;position:absolute;right:15%;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{-webkit-filter:invert(1) grayscale(100);filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes spinner-border{to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.spinner-border{-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite;border:.25em solid;border-radius:50%;border-right:.25em solid transparent;display:inline-block;height:2rem;vertical-align:-.125em;width:2rem}.spinner-border-sm{border-width:.2em;height:1rem;width:1rem}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite;background-color:currentColor;border-radius:50%;display:inline-block;height:2rem;opacity:0;vertical-align:-.125em;width:2rem}.spinner-grow-sm{height:1rem;width:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.offcanvas{background-clip:padding-box;background-color:#fff;bottom:0;display:flex;flex-direction:column;max-width:100%;outline:0;position:fixed;transition:-webkit-transform .3s ease-in-out;transition:transform .3s ease-in-out;transition:transform .3s ease-in-out,-webkit-transform .3s ease-in-out;visibility:hidden;z-index:1045}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas-backdrop{background-color:#000;height:100vh;left:0;position:fixed;top:0;width:100vw;z-index:1040}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{align-items:center;display:flex;justify-content:space-between;padding:1rem}.offcanvas-header .btn-close{margin-bottom:-.5rem;margin-right:-.5rem;margin-top:-.5rem;padding:.5rem}.offcanvas-title{line-height:1.5;margin-bottom:0}.offcanvas-body{flex-grow:1;overflow-y:auto;padding:1rem}.offcanvas-start{border-right:1px solid rgba(0,0,0,.2);left:0;top:0;-webkit-transform:translateX(-100%);transform:translateX(-100%);width:400px}.offcanvas-end{border-left:1px solid rgba(0,0,0,.2);right:0;top:0;-webkit-transform:translateX(100%);transform:translateX(100%);width:400px}.offcanvas-top{border-bottom:1px solid rgba(0,0,0,.2);top:0;-webkit-transform:translateY(-100%);transform:translateY(-100%)}.offcanvas-bottom,.offcanvas-top{height:30vh;left:0;max-height:100%;right:0}.offcanvas-bottom{border-top:1px solid rgba(0,0,0,.2);-webkit-transform:translateY(100%);transform:translateY(100%)}.offcanvas.show{-webkit-transform:none;transform:none}.placeholder{background-color:currentColor;cursor:wait;display:inline-block;min-height:1em;opacity:.5;vertical-align:middle}.placeholder.btn:before{content:"";display:inline-block}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{-webkit-animation:placeholder-glow 2s ease-in-out infinite;animation:placeholder-glow 2s ease-in-out infinite}@-webkit-keyframes placeholder-glow{50%{opacity:.2}}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-animation:placeholder-wave 2s linear infinite;animation:placeholder-wave 2s linear infinite;-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%}@-webkit-keyframes placeholder-wave{to{-webkit-mask-position:-200% 0;mask-position:-200% 0}}@keyframes placeholder-wave{to{-webkit-mask-position:-200% 0;mask-position:-200% 0}}.clearfix:after{clear:both;content:"";display:block}.link-primary{color:#0d6efd}.link-primary:focus,.link-primary:hover{color:#0a58ca}.link-secondary{color:#6c757d}.link-secondary:focus,.link-secondary:hover{color:#565e64}.link-success{color:#198754}.link-success:focus,.link-success:hover{color:#146c43}.link-info{color:#0dcaf0}.link-info:focus,.link-info:hover{color:#3dd5f3}.link-warning{color:#ffc107}.link-warning:focus,.link-warning:hover{color:#ffcd39}.link-danger{color:#dc3545}.link-danger:focus,.link-danger:hover{color:#b02a37}.link-light{color:#f8f9fa}.link-light:focus,.link-light:hover{color:#f9fafb}.link-dark{color:#212529}.link-dark:focus,.link-dark:hover{color:#1a1e21}.ratio{position:relative;width:100%}.ratio:before{content:"";display:block;padding-top:var(--bs-aspect-ratio)}.ratio>*{height:100%;left:0;position:absolute;top:0;width:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:75%}.ratio-16x9{--bs-aspect-ratio:56.25%}.ratio-21x9{--bs-aspect-ratio:42.8571428571%}.fixed-top{top:0}.fixed-bottom,.fixed-top{left:0;position:fixed;right:0;z-index:1030}.fixed-bottom{bottom:0}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.hstack{align-items:center;flex-direction:row}.hstack,.vstack{align-self:stretch;display:flex}.vstack{flex:1 1 auto;flex-direction:column}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){clip:rect(0,0,0,0)!important;border:0!important;height:1px!important;margin:-1px!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:1px!important}.stretched-link:after{bottom:0;content:"";left:0;position:absolute;right:0;top:0;z-index:1}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{align-self:stretch;background-color:currentColor;display:inline-block;min-height:1em;opacity:.25;width:1px}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{-webkit-transform:translate(-50%,-50%)!important;transform:translate(-50%,-50%)!important}.translate-middle-x{-webkit-transform:translateX(-50%)!important;transform:translateX(-50%)!important}.translate-middle-y{-webkit-transform:translateY(-50%)!important;transform:translateY(-50%)!important}.border{border:1px solid #dee2e6!important}.border-0{border:0!important}.border-top{border-top:1px solid #dee2e6!important}.border-top-0{border-top:0!important}.border-end{border-right:1px solid #dee2e6!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:1px solid #dee2e6!important}.border-start-0{border-left:0!important}.border-primary{border-color:#0d6efd!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#198754!important}.border-info{border-color:#0dcaf0!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#212529!important}.border-white{border-color:#fff!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-left:0!important;margin-right:0!important}.mx-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-3{margin-left:1rem!important;margin-right:1rem!important}.mx-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-5{margin-left:3rem!important;margin-right:3rem!important}.mx-auto{margin-left:auto!important;margin-right:auto!important}.my-0{margin-bottom:0!important;margin-top:0!important}.my-1{margin-bottom:.25rem!important;margin-top:.25rem!important}.my-2{margin-bottom:.5rem!important;margin-top:.5rem!important}.my-3{margin-bottom:1rem!important;margin-top:1rem!important}.my-4{margin-bottom:1.5rem!important;margin-top:1.5rem!important}.my-5{margin-bottom:3rem!important;margin-top:3rem!important}.my-auto{margin-bottom:auto!important;margin-top:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-left:0!important;padding-right:0!important}.px-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-3{padding-left:1rem!important;padding-right:1rem!important}.px-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-5{padding-left:3rem!important;padding-right:3rem!important}.py-0{padding-bottom:0!important;padding-top:0!important}.py-1{padding-bottom:.25rem!important;padding-top:.25rem!important}.py-2{padding-bottom:.5rem!important;padding-top:.5rem!important}.py-3{padding-bottom:1rem!important;padding-top:1rem!important}.py-4{padding-bottom:1.5rem!important;padding-top:1.5rem!important}.py-5{padding-bottom:3rem!important;padding-top:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.font-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace!important;font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-light{font-weight:300!important}.fw-lighter{font-weight:lighter!important}.fw-normal{font-weight:400!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(13,110,253,var(--bs-text-opacity))!important;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(108,117,125,var(--bs-text-opacity))!important;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(25,135,84,var(--bs-text-opacity))!important;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(13,202,240,var(--bs-text-opacity))!important;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(255,193,7,var(--bs-text-opacity))!important;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(220,53,69,var(--bs-text-opacity))!important;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(248,249,250,var(--bs-text-opacity))!important;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(33,37,41,var(--bs-text-opacity))!important;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(0,0,0,var(--bs-text-opacity))!important;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(255,255,255,var(--bs-text-opacity))!important;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(33,37,41,var(--bs-text-opacity))!important;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:#6c757d!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:hsla(0,0%,100%,.5)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(13,110,253,var(--bs-bg-opacity))!important;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(108,117,125,var(--bs-bg-opacity))!important;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(25,135,84,var(--bs-bg-opacity))!important;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(13,202,240,var(--bs-bg-opacity))!important;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(255,193,7,var(--bs-bg-opacity))!important;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(220,53,69,var(--bs-bg-opacity))!important;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(248,249,250,var(--bs-bg-opacity))!important;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(33,37,41,var(--bs-bg-opacity))!important;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(0,0,0,var(--bs-bg-opacity))!important;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(255,255,255,var(--bs-bg-opacity))!important;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(255,255,255,var(--bs-bg-opacity))!important;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-gradient{background-image:linear-gradient(180deg,hsla(0,0%,100%,.15),hsla(0,0%,100%,0))!important;background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-ms-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:.25rem!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:.2rem!important}.rounded-2{border-radius:.25rem!important}.rounded-3{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-top{border-top-left-radius:.25rem!important}.rounded-end,.rounded-top{border-top-right-radius:.25rem!important}.rounded-bottom,.rounded-end{border-bottom-right-radius:.25rem!important}.rounded-bottom,.rounded-start{border-bottom-left-radius:.25rem!important}.rounded-start{border-top-left-radius:.25rem!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-left:0!important;margin-right:0!important}.mx-sm-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-sm-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-sm-3{margin-left:1rem!important;margin-right:1rem!important}.mx-sm-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-sm-5{margin-left:3rem!important;margin-right:3rem!important}.mx-sm-auto{margin-left:auto!important;margin-right:auto!important}.my-sm-0{margin-bottom:0!important;margin-top:0!important}.my-sm-1{margin-bottom:.25rem!important;margin-top:.25rem!important}.my-sm-2{margin-bottom:.5rem!important;margin-top:.5rem!important}.my-sm-3{margin-bottom:1rem!important;margin-top:1rem!important}.my-sm-4{margin-bottom:1.5rem!important;margin-top:1.5rem!important}.my-sm-5{margin-bottom:3rem!important;margin-top:3rem!important}.my-sm-auto{margin-bottom:auto!important;margin-top:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-left:0!important;padding-right:0!important}.px-sm-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-sm-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-sm-3{padding-left:1rem!important;padding-right:1rem!important}.px-sm-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-sm-5{padding-left:3rem!important;padding-right:3rem!important}.py-sm-0{padding-bottom:0!important;padding-top:0!important}.py-sm-1{padding-bottom:.25rem!important;padding-top:.25rem!important}.py-sm-2{padding-bottom:.5rem!important;padding-top:.5rem!important}.py-sm-3{padding-bottom:1rem!important;padding-top:1rem!important}.py-sm-4{padding-bottom:1.5rem!important;padding-top:1.5rem!important}.py-sm-5{padding-bottom:3rem!important;padding-top:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-left:0!important;margin-right:0!important}.mx-md-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-md-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-md-3{margin-left:1rem!important;margin-right:1rem!important}.mx-md-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-md-5{margin-left:3rem!important;margin-right:3rem!important}.mx-md-auto{margin-left:auto!important;margin-right:auto!important}.my-md-0{margin-bottom:0!important;margin-top:0!important}.my-md-1{margin-bottom:.25rem!important;margin-top:.25rem!important}.my-md-2{margin-bottom:.5rem!important;margin-top:.5rem!important}.my-md-3{margin-bottom:1rem!important;margin-top:1rem!important}.my-md-4{margin-bottom:1.5rem!important;margin-top:1.5rem!important}.my-md-5{margin-bottom:3rem!important;margin-top:3rem!important}.my-md-auto{margin-bottom:auto!important;margin-top:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-left:0!important;padding-right:0!important}.px-md-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-md-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-md-3{padding-left:1rem!important;padding-right:1rem!important}.px-md-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-md-5{padding-left:3rem!important;padding-right:3rem!important}.py-md-0{padding-bottom:0!important;padding-top:0!important}.py-md-1{padding-bottom:.25rem!important;padding-top:.25rem!important}.py-md-2{padding-bottom:.5rem!important;padding-top:.5rem!important}.py-md-3{padding-bottom:1rem!important;padding-top:1rem!important}.py-md-4{padding-bottom:1.5rem!important;padding-top:1.5rem!important}.py-md-5{padding-bottom:3rem!important;padding-top:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-left:0!important;margin-right:0!important}.mx-lg-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-lg-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-lg-3{margin-left:1rem!important;margin-right:1rem!important}.mx-lg-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-lg-5{margin-left:3rem!important;margin-right:3rem!important}.mx-lg-auto{margin-left:auto!important;margin-right:auto!important}.my-lg-0{margin-bottom:0!important;margin-top:0!important}.my-lg-1{margin-bottom:.25rem!important;margin-top:.25rem!important}.my-lg-2{margin-bottom:.5rem!important;margin-top:.5rem!important}.my-lg-3{margin-bottom:1rem!important;margin-top:1rem!important}.my-lg-4{margin-bottom:1.5rem!important;margin-top:1.5rem!important}.my-lg-5{margin-bottom:3rem!important;margin-top:3rem!important}.my-lg-auto{margin-bottom:auto!important;margin-top:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-left:0!important;padding-right:0!important}.px-lg-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-lg-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-lg-3{padding-left:1rem!important;padding-right:1rem!important}.px-lg-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-lg-5{padding-left:3rem!important;padding-right:3rem!important}.py-lg-0{padding-bottom:0!important;padding-top:0!important}.py-lg-1{padding-bottom:.25rem!important;padding-top:.25rem!important}.py-lg-2{padding-bottom:.5rem!important;padding-top:.5rem!important}.py-lg-3{padding-bottom:1rem!important;padding-top:1rem!important}.py-lg-4{padding-bottom:1.5rem!important;padding-top:1.5rem!important}.py-lg-5{padding-bottom:3rem!important;padding-top:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-left:0!important;margin-right:0!important}.mx-xl-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-xl-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-xl-3{margin-left:1rem!important;margin-right:1rem!important}.mx-xl-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-xl-5{margin-left:3rem!important;margin-right:3rem!important}.mx-xl-auto{margin-left:auto!important;margin-right:auto!important}.my-xl-0{margin-bottom:0!important;margin-top:0!important}.my-xl-1{margin-bottom:.25rem!important;margin-top:.25rem!important}.my-xl-2{margin-bottom:.5rem!important;margin-top:.5rem!important}.my-xl-3{margin-bottom:1rem!important;margin-top:1rem!important}.my-xl-4{margin-bottom:1.5rem!important;margin-top:1.5rem!important}.my-xl-5{margin-bottom:3rem!important;margin-top:3rem!important}.my-xl-auto{margin-bottom:auto!important;margin-top:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-left:0!important;padding-right:0!important}.px-xl-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-xl-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-xl-3{padding-left:1rem!important;padding-right:1rem!important}.px-xl-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-xl-5{padding-left:3rem!important;padding-right:3rem!important}.py-xl-0{padding-bottom:0!important;padding-top:0!important}.py-xl-1{padding-bottom:.25rem!important;padding-top:.25rem!important}.py-xl-2{padding-bottom:.5rem!important;padding-top:.5rem!important}.py-xl-3{padding-bottom:1rem!important;padding-top:1rem!important}.py-xl-4{padding-bottom:1.5rem!important;padding-top:1.5rem!important}.py-xl-5{padding-bottom:3rem!important;padding-top:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-left:0!important;margin-right:0!important}.mx-xxl-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-xxl-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-xxl-3{margin-left:1rem!important;margin-right:1rem!important}.mx-xxl-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-xxl-5{margin-left:3rem!important;margin-right:3rem!important}.mx-xxl-auto{margin-left:auto!important;margin-right:auto!important}.my-xxl-0{margin-bottom:0!important;margin-top:0!important}.my-xxl-1{margin-bottom:.25rem!important;margin-top:.25rem!important}.my-xxl-2{margin-bottom:.5rem!important;margin-top:.5rem!important}.my-xxl-3{margin-bottom:1rem!important;margin-top:1rem!important}.my-xxl-4{margin-bottom:1.5rem!important;margin-top:1.5rem!important}.my-xxl-5{margin-bottom:3rem!important;margin-top:3rem!important}.my-xxl-auto{margin-bottom:auto!important;margin-top:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-left:0!important;padding-right:0!important}.px-xxl-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-xxl-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-xxl-3{padding-left:1rem!important;padding-right:1rem!important}.px-xxl-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-xxl-5{padding-left:3rem!important;padding-right:3rem!important}.py-xxl-0{padding-bottom:0!important;padding-top:0!important}.py-xxl-1{padding-bottom:.25rem!important;padding-top:.25rem!important}.py-xxl-2{padding-bottom:.5rem!important;padding-top:.5rem!important}.py-xxl-3{padding-bottom:1rem!important;padding-top:1rem!important}.py-xxl-4{padding-bottom:1.5rem!important;padding-top:1.5rem!important}.py-xxl-5{padding-bottom:3rem!important;padding-top:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}}.App{text-align:center}.App-logo{height:40vmin;pointer-events:none}@media (prefers-reduced-motion:no-preference){.App-logo{-webkit-animation:App-logo-spin 20s linear infinite;animation:App-logo-spin 20s linear infinite}}fieldset{border:2px groove silver!important;border-image:none!important;border-image:initial!important;padding:15px!important}legend{font-size:1em!important;font-weight:700;margin-bottom:24px}.App-header{align-items:center;background-color:#282c34;color:#fff;display:flex;flex-direction:column;font-size:calc(10px + 2vmin);justify-content:center;min-height:100vh}.App-link{color:#61dafb}@-webkit-keyframes App-logo-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes App-logo-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.container-fixed{margin-top:6rem}.card-container{border:1px solid #ccc;cursor:pointer;font-size:.8em;margin-bottom:15px;padding:15px}.card-status{font-size:.6em}.card-container.purple{background:#610dad;color:#fff}.card-status .yellow{background:#ffc107}.card-status .red,.card-status .yellow{border-radius:12px;height:12px;margin-right:5px;width:12px}.card-status .red{background:#dc3545}.card-status .blue{background:#0dcaf0}.card-status .blue,.card-status .green{border-radius:12px;height:12px;margin-right:5px;width:12px}.card-status .green{background:#0de480}.card-status .gray{background:#ccc;border-radius:12px;height:12px;margin-right:5px;width:12px}table,table tr,table tr td,table tr th{border:1px solid #0dcaf0}table tr td,table tr th{color:#0dcaf0;font-size:11px;padding:5px 15px}table tr td{color:#000}table .children{padding-left:40px}table .group td{font-style:italic}select{height:40px;margin-left:5px;min-width:200px;padding:5px}a{cursor:pointer;text-decoration:underline!important}input[type=text]{height:40px}input[type=text],textarea{margin-left:5px;padding-left:15px;padding-right:15px}textarea{height:80px;width:100%}input[type=email],input[type=number],input[type=password]{height:40px;margin-left:5px;padding-left:15px;padding-right:15px}input[type=checkbox]{margin-left:8px}.clickable{cursor:pointer}table.medical th{color:#000;font-size:16px}table.medical td{color:#000;font-size:14px}table tr,table.medical,table.medical tr td,table.medical tr th{border-color:#000}.personnel-info-table .red{background:#dc3545;color:#191717}.personnel-info-table .red button{color:#fff}.personnel-info-table .dark-red{background:#7c0d0e;color:#fff}.personnel-info-table .light-red{background:#864f50;color:#fff}.personnel-info-table .dark-gray{background:#777;color:#fff}.personnel-info-table .light-green{background:#0de480;color:#fff}.personnel-info-table .light-green button{color:#fff}.personnel-info-table .dark-green{background:#198754;color:#fff}.personnel-info-table .dark-blue{background:#0d6efd;color:#fff}.personnel-info-table .light-blue{background:#0dcaf0;color:#fff}.personnel-info-table .black{background:#000;color:#fff}.personnel-info-table .yellow{background:#ff0;color:#fff}.personnel-info-table .white{background:#fff;color:#000}.medical ul li{color:#0d6efd;cursor:pointer;font-size:18px;list-style:none;margin-bottom:8px}.comments-text{height:150px;width:200px}.medical ul li:focus,.medical ul li:hover{color:#0d6efd;cursor:pointer;font-size:18px;list-style:none;margin-bottom:8px;text-decoration:underline}.report-field-value{background:#eee;padding:4px}.selected{background:#ffc107}.customers-container{background:#eee;padding:20px;width:100%}.customers-dnd-item-container{align-items:center;border:1px dotted #777;cursor:move;display:flex;font-size:13px;margin-bottom:15px;padding:15px;width:100%}.customer-dnd-img{height:18px;margin-right:20px;width:18px}.customer-dnd-item-content{font-size:11px;padding-left:20px}.option-item{align-items:center;display:flex;margin-bottom:15px;padding:15px}.customer-delete-btn{margin-left:auto}.react-datepicker__input-container input{height:35px;width:100%}.admin-nav a{text-decoration:none!important}.login{margin:auto;text-align:center}.login-container{background:#eee;padding:40px 5px}.btn-login,.login-container input{min-width:300px}.create-route-container{background:#eee;padding:15px}.landing{color:#0d6efd}.landing-img{background-image:url(/static/media/background.d0e107221150b4c16901.jpg);background-position:100% 0;background-repeat:no-repeat;height:100%;position:fixed;right:0;top:0;width:100%;z-index:-1}.landing-content-title{height:100vh;padding-top:5%}.landing-title{width:100%}.landing-content{margin-bottom:40px;margin-left:20px;margin-top:40px}.landing-content a{margin-left:15px}.landing-content h6{margin-bottom:20px}.logo{align-items:center;display:flex;left:40px;position:absolute;top:30px}.logo-suffix{color:orange;font-size:14px;font-weight:700;margin-top:-3px}.btn-medical{padding-left:3em!important;padding-right:3em!important}.btn-medical a{color:#fff;margin-left:0;text-decoration:none!important}.logo img{height:50px}.customers-pagination{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;list-style-type:none;margin-bottom:2rem;padding:0}.customers-pagination li a{background:transparent;border-radius:7px;color:#777;cursor:pointer;padding:.1rem .8rem}.customers-pagination li{border:none}.customers-pagination li a{border:none;text-decoration:none!important}.customers-pagination li.break a,.customers-pagination li.next a,.customers-pagination li.previous a{background:transparent;border-color:transparent}.customers-pagination li.active a{background-color:#0366d6!important;border-color:transparent;color:#fff;min-width:32px}.customers-pagination li.disabled a{background:transparent!important;color:grey}.customers-pagination li.disable,.customers-pagination li.disabled a{background:transparent;cursor:default}.modal-fullscreen-xxl-down{height:100%;margin:0;max-width:none;width:100vw}.table-button-container{min-width:320px}.sigCanvas{border:1px solid #333}.transfer-select{height:30px;margin-right:5px;min-width:50px!important;padding-bottom:0;padding-top:0}.spinner-overlay{align-items:center;background:#fff;display:flex;height:100%;justify-content:center;left:0;opacity:.8;position:fixed;top:0;width:100%}@media only screen and (min-width:1200px){.container{max-width:1200px}}@media only screen and (max-width:768px){.landing{color:#000}}@media only screen and (min-width:2000px){.container{max-width:1920px}}@media print{@page{size:landscape}}@media print{.noprint{display:none!important}.container{margin-left:0;margin-right:0}}.react-clock{display:block;position:relative}.react-clock,.react-clock *,.react-clock :after,.react-clock :before{box-sizing:border-box}.react-clock__face{border:1px solid #000;border-radius:50%;bottom:0;left:0;position:absolute;right:0;top:0}.react-clock__hand{bottom:0;left:50%;position:absolute;right:50%;top:0}.react-clock__hand__body{background-color:#000;position:absolute;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.react-clock__mark{bottom:0;left:50%;position:absolute;right:50%;top:0}.react-clock__mark__body{background-color:#000;position:absolute;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.react-clock__mark__number{left:-40px;position:absolute;text-align:center;width:80px}.react-clock__second-hand__body{background-color:red}.react-time-picker{display:inline-flex;position:relative}.react-time-picker,.react-time-picker *,.react-time-picker :after,.react-time-picker :before{box-sizing:border-box}.react-time-picker--disabled{background-color:#f0f0f0;color:#6d6d6d}.react-time-picker__wrapper{border:thin solid gray;display:flex;flex-grow:1;flex-shrink:0}.react-time-picker__inputGroup{box-sizing:content-box;flex-grow:1;min-width:calc(12px + 3.674em);padding:0 2px}.react-time-picker__inputGroup__divider{padding:1px 0;white-space:pre}.react-time-picker__inputGroup__input{-moz-appearance:textfield;background:none;border:0;box-sizing:content-box;font:inherit;height:100%;min-width:.54em;padding:0 1px;position:relative}.react-time-picker__inputGroup__input::-webkit-inner-spin-button,.react-time-picker__inputGroup__input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.react-time-picker__inputGroup__input:invalid{background:rgba(255,0,0,.1)}.react-time-picker__inputGroup__input--hasLeadingZero{margin-left:-.54em;padding-left:calc(1px + .54em)}.react-time-picker__inputGroup__amPm{-moz-appearance:menulist;font:inherit}.react-time-picker__button{background:transparent;border:0;padding:4px 6px}.react-time-picker__button:enabled{cursor:pointer}.react-time-picker__button:enabled:focus .react-time-picker__button__icon,.react-time-picker__button:enabled:hover .react-time-picker__button__icon{stroke:#0078d7}.react-time-picker__button:disabled .react-time-picker__button__icon{stroke:#6d6d6d}.react-time-picker__button svg{display:inherit}.react-time-picker__clock{background-color:#fff;border:thin solid #a0a096;height:200px;left:0;max-width:100vw;padding:25px;position:absolute;top:100%;width:200px;z-index:1}.react-time-picker__clock--closed{display:none}.react-datepicker__month-read-view--down-arrow,.react-datepicker__month-year-read-view--down-arrow,.react-datepicker__navigation-icon:before,.react-datepicker__year-read-view--down-arrow{border-color:#ccc;border-style:solid;border-width:3px 3px 0 0;content:"";display:block;height:9px;position:absolute;top:6px;width:9px}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle{margin-left:-4px;position:absolute;width:0}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before{border:8px solid transparent;box-sizing:content-box;content:"";height:0;left:-8px;position:absolute;width:1px;z-index:-1}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before{border-bottom-color:#aeaeae}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle{margin-top:-8px;top:0}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before{border-bottom-color:#f0f0f0;border-top:none}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:after{top:0}.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle:before{border-bottom-color:#aeaeae;top:-1px}.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle{bottom:0;margin-bottom:-8px}.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:after,.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before{border-bottom:none;border-top-color:#fff}.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:after{bottom:0}.react-datepicker-popper[data-placement^=top] .react-datepicker__triangle:before{border-top-color:#aeaeae;bottom:-1px}.react-datepicker-wrapper{border:0;display:inline-block;padding:0;width:100%}.react-datepicker{background-color:#fff;border:1px solid #aeaeae;border-radius:.3rem;color:#000;display:inline-block;font-family:Helvetica Neue,helvetica,arial,sans-serif;font-size:.8rem;position:relative}.react-datepicker--time-only .react-datepicker__triangle{left:35px}.react-datepicker--time-only .react-datepicker__time-container{border-left:0}.react-datepicker--time-only .react-datepicker__time,.react-datepicker--time-only .react-datepicker__time-box{border-bottom-left-radius:.3rem;border-bottom-right-radius:.3rem}.react-datepicker__triangle{left:50px;position:absolute}.react-datepicker-popper{z-index:1}.react-datepicker-popper[data-placement^=bottom]{padding-top:10px}.react-datepicker-popper[data-placement=bottom-end] .react-datepicker__triangle,.react-datepicker-popper[data-placement=top-end] .react-datepicker__triangle{left:auto;right:50px}.react-datepicker-popper[data-placement^=top]{padding-bottom:10px}.react-datepicker-popper[data-placement^=right]{padding-left:8px}.react-datepicker-popper[data-placement^=right] .react-datepicker__triangle{left:auto;right:42px}.react-datepicker-popper[data-placement^=left]{padding-right:8px}.react-datepicker-popper[data-placement^=left] .react-datepicker__triangle{left:42px;right:auto}.react-datepicker__header{background-color:#f0f0f0;border-bottom:1px solid #aeaeae;border-top-left-radius:.3rem;padding:8px 0;position:relative;text-align:center}.react-datepicker__header--time{padding-bottom:8px;padding-left:5px;padding-right:5px}.react-datepicker__header--time:not(.react-datepicker__header--time--only){border-top-left-radius:0}.react-datepicker__header:not(.react-datepicker__header--has-time-select){border-top-right-radius:.3rem}.react-datepicker__month-dropdown-container--scroll,.react-datepicker__month-dropdown-container--select,.react-datepicker__month-year-dropdown-container--scroll,.react-datepicker__month-year-dropdown-container--select,.react-datepicker__year-dropdown-container--scroll,.react-datepicker__year-dropdown-container--select{display:inline-block;margin:0 2px}.react-datepicker-time__header,.react-datepicker-year-header,.react-datepicker__current-month{color:#000;font-size:.944rem;font-weight:700;margin-top:0}.react-datepicker-time__header{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.react-datepicker__navigation{align-items:center;background:none;border:none;cursor:pointer;display:flex;height:32px;justify-content:center;overflow:hidden;padding:0;position:absolute;text-align:center;text-indent:-999em;top:2px;width:32px;z-index:1}.react-datepicker__navigation--previous{left:2px}.react-datepicker__navigation--next{right:2px}.react-datepicker__navigation--next--with-time:not(.react-datepicker__navigation--next--with-today-button){right:85px}.react-datepicker__navigation--years{display:block;margin-left:auto;margin-right:auto;position:relative;top:0}.react-datepicker__navigation--years-previous{top:4px}.react-datepicker__navigation--years-upcoming{top:-4px}.react-datepicker__navigation:hover :before{border-color:#a6a6a6}.react-datepicker__navigation-icon{font-size:20px;position:relative;top:-1px;width:0}.react-datepicker__navigation-icon--next{left:-2px}.react-datepicker__navigation-icon--next:before{left:-7px;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.react-datepicker__navigation-icon--previous{right:-2px}.react-datepicker__navigation-icon--previous:before{right:-7px;-webkit-transform:rotate(225deg);transform:rotate(225deg)}.react-datepicker__month-container{float:left}.react-datepicker__year{margin:.4rem;text-align:center}.react-datepicker__year-wrapper{display:flex;flex-wrap:wrap;max-width:180px}.react-datepicker__year .react-datepicker__year-text{display:inline-block;margin:2px;width:4rem}.react-datepicker__month{margin:.4rem;text-align:center}.react-datepicker__month .react-datepicker__month-text,.react-datepicker__month .react-datepicker__quarter-text{display:inline-block;margin:2px;width:4rem}.react-datepicker__input-time-container{clear:both;float:left;margin:5px 0 10px 15px;text-align:left;width:100%}.react-datepicker__input-time-container .react-datepicker-time__caption,.react-datepicker__input-time-container .react-datepicker-time__input-container{display:inline-block}.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input{display:inline-block;margin-left:10px}.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input{width:auto}.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time]::-webkit-inner-spin-button,.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__input input[type=time]{-moz-appearance:textfield}.react-datepicker__input-time-container .react-datepicker-time__input-container .react-datepicker-time__delimiter{display:inline-block;margin-left:5px}.react-datepicker__time-container{border-left:1px solid #aeaeae;float:right;width:85px}.react-datepicker__time-container--with-today-button{border:1px solid #aeaeae;border-radius:.3rem;display:inline;position:absolute;right:-72px;top:0}.react-datepicker__time-container .react-datepicker__time{background:#fff;border-bottom-right-radius:.3rem;position:relative}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box{border-bottom-right-radius:.3rem;margin:0 auto;overflow-x:hidden;text-align:center;width:85px}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list{box-sizing:content-box;height:calc(195px + .85rem);list-style:none;margin:0;overflow-y:scroll;padding-left:0;padding-right:0;width:100%}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item{height:30px;padding:5px 10px;white-space:nowrap}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item:hover{background-color:#f0f0f0;cursor:pointer}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected{background-color:#216ba5;color:#fff;font-weight:700}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--selected:hover{background-color:#216ba5}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled{color:#ccc}.react-datepicker__time-container .react-datepicker__time .react-datepicker__time-box ul.react-datepicker__time-list li.react-datepicker__time-list-item--disabled:hover{background-color:transparent;cursor:default}.react-datepicker__week-number{color:#ccc;display:inline-block;line-height:1.7rem;margin:.166rem;text-align:center;width:1.7rem}.react-datepicker__week-number.react-datepicker__week-number--clickable{cursor:pointer}.react-datepicker__week-number.react-datepicker__week-number--clickable:hover{background-color:#f0f0f0;border-radius:.3rem}.react-datepicker__day-names,.react-datepicker__week{white-space:nowrap}.react-datepicker__day-names{margin-bottom:-8px}.react-datepicker__day,.react-datepicker__day-name,.react-datepicker__time-name{color:#000;display:inline-block;line-height:1.7rem;margin:.166rem;text-align:center;width:1.7rem}.react-datepicker__month--in-range,.react-datepicker__month--in-selecting-range,.react-datepicker__month--selected,.react-datepicker__quarter--in-range,.react-datepicker__quarter--in-selecting-range,.react-datepicker__quarter--selected{background-color:#216ba5;border-radius:.3rem;color:#fff}.react-datepicker__month--in-range:hover,.react-datepicker__month--in-selecting-range:hover,.react-datepicker__month--selected:hover,.react-datepicker__quarter--in-range:hover,.react-datepicker__quarter--in-selecting-range:hover,.react-datepicker__quarter--selected:hover{background-color:#1d5d90}.react-datepicker__month--disabled,.react-datepicker__quarter--disabled{color:#ccc;pointer-events:none}.react-datepicker__month--disabled:hover,.react-datepicker__quarter--disabled:hover{background-color:transparent;cursor:default}.react-datepicker__day,.react-datepicker__month-text,.react-datepicker__quarter-text,.react-datepicker__year-text{cursor:pointer}.react-datepicker__day:hover,.react-datepicker__month-text:hover,.react-datepicker__quarter-text:hover,.react-datepicker__year-text:hover{background-color:#f0f0f0;border-radius:.3rem}.react-datepicker__day--today,.react-datepicker__month-text--today,.react-datepicker__quarter-text--today,.react-datepicker__year-text--today{font-weight:700}.react-datepicker__day--highlighted,.react-datepicker__month-text--highlighted,.react-datepicker__quarter-text--highlighted,.react-datepicker__year-text--highlighted{background-color:#3dcc4a;border-radius:.3rem;color:#fff}.react-datepicker__day--highlighted:hover,.react-datepicker__month-text--highlighted:hover,.react-datepicker__quarter-text--highlighted:hover,.react-datepicker__year-text--highlighted:hover{background-color:#32be3f}.react-datepicker__day--highlighted-custom-1,.react-datepicker__month-text--highlighted-custom-1,.react-datepicker__quarter-text--highlighted-custom-1,.react-datepicker__year-text--highlighted-custom-1{color:#f0f}.react-datepicker__day--highlighted-custom-2,.react-datepicker__month-text--highlighted-custom-2,.react-datepicker__quarter-text--highlighted-custom-2,.react-datepicker__year-text--highlighted-custom-2{color:green}.react-datepicker__day--in-range,.react-datepicker__day--in-selecting-range,.react-datepicker__day--selected,.react-datepicker__month-text--in-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__month-text--selected,.react-datepicker__quarter-text--in-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__quarter-text--selected,.react-datepicker__year-text--in-range,.react-datepicker__year-text--in-selecting-range,.react-datepicker__year-text--selected{background-color:#216ba5;border-radius:.3rem;color:#fff}.react-datepicker__day--in-range:hover,.react-datepicker__day--in-selecting-range:hover,.react-datepicker__day--selected:hover,.react-datepicker__month-text--in-range:hover,.react-datepicker__month-text--in-selecting-range:hover,.react-datepicker__month-text--selected:hover,.react-datepicker__quarter-text--in-range:hover,.react-datepicker__quarter-text--in-selecting-range:hover,.react-datepicker__quarter-text--selected:hover,.react-datepicker__year-text--in-range:hover,.react-datepicker__year-text--in-selecting-range:hover,.react-datepicker__year-text--selected:hover{background-color:#1d5d90}.react-datepicker__day--keyboard-selected,.react-datepicker__month-text--keyboard-selected,.react-datepicker__quarter-text--keyboard-selected,.react-datepicker__year-text--keyboard-selected{background-color:#2579ba;border-radius:.3rem;color:#fff}.react-datepicker__day--keyboard-selected:hover,.react-datepicker__month-text--keyboard-selected:hover,.react-datepicker__quarter-text--keyboard-selected:hover,.react-datepicker__year-text--keyboard-selected:hover{background-color:#1d5d90}.react-datepicker__day--in-selecting-range:not(.react-datepicker__day--in-range,.react-datepicker__month-text--in-range,.react-datepicker__quarter-text--in-range,.react-datepicker__year-text--in-range),.react-datepicker__month-text--in-selecting-range:not(.react-datepicker__day--in-range,.react-datepicker__month-text--in-range,.react-datepicker__quarter-text--in-range,.react-datepicker__year-text--in-range),.react-datepicker__quarter-text--in-selecting-range:not(.react-datepicker__day--in-range,.react-datepicker__month-text--in-range,.react-datepicker__quarter-text--in-range,.react-datepicker__year-text--in-range),.react-datepicker__year-text--in-selecting-range:not(.react-datepicker__day--in-range,.react-datepicker__month-text--in-range,.react-datepicker__quarter-text--in-range,.react-datepicker__year-text--in-range){background-color:rgba(33,107,165,.5)}.react-datepicker__month--selecting-range .react-datepicker__day--in-range:not(.react-datepicker__day--in-selecting-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__year-text--in-selecting-range),.react-datepicker__month--selecting-range .react-datepicker__month-text--in-range:not(.react-datepicker__day--in-selecting-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__year-text--in-selecting-range),.react-datepicker__month--selecting-range .react-datepicker__quarter-text--in-range:not(.react-datepicker__day--in-selecting-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__year-text--in-selecting-range),.react-datepicker__month--selecting-range .react-datepicker__year-text--in-range:not(.react-datepicker__day--in-selecting-range,.react-datepicker__month-text--in-selecting-range,.react-datepicker__quarter-text--in-selecting-range,.react-datepicker__year-text--in-selecting-range){background-color:#f0f0f0;color:#000}.react-datepicker__day--disabled,.react-datepicker__month-text--disabled,.react-datepicker__quarter-text--disabled,.react-datepicker__year-text--disabled{color:#ccc;cursor:default}.react-datepicker__day--disabled:hover,.react-datepicker__month-text--disabled:hover,.react-datepicker__quarter-text--disabled:hover,.react-datepicker__year-text--disabled:hover{background-color:transparent}.react-datepicker__month-text.react-datepicker__month--in-range:hover,.react-datepicker__month-text.react-datepicker__month--selected:hover,.react-datepicker__month-text.react-datepicker__quarter--in-range:hover,.react-datepicker__month-text.react-datepicker__quarter--selected:hover,.react-datepicker__quarter-text.react-datepicker__month--in-range:hover,.react-datepicker__quarter-text.react-datepicker__month--selected:hover,.react-datepicker__quarter-text.react-datepicker__quarter--in-range:hover,.react-datepicker__quarter-text.react-datepicker__quarter--selected:hover{background-color:#216ba5}.react-datepicker__month-text:hover,.react-datepicker__quarter-text:hover{background-color:#f0f0f0}.react-datepicker__input-container{display:inline-block;position:relative;width:100%}.react-datepicker__month-read-view,.react-datepicker__month-year-read-view,.react-datepicker__year-read-view{border:1px solid transparent;border-radius:.3rem;position:relative}.react-datepicker__month-read-view:hover,.react-datepicker__month-year-read-view:hover,.react-datepicker__year-read-view:hover{cursor:pointer}.react-datepicker__month-read-view:hover .react-datepicker__month-read-view--down-arrow,.react-datepicker__month-read-view:hover .react-datepicker__year-read-view--down-arrow,.react-datepicker__month-year-read-view:hover .react-datepicker__month-read-view--down-arrow,.react-datepicker__month-year-read-view:hover .react-datepicker__year-read-view--down-arrow,.react-datepicker__year-read-view:hover .react-datepicker__month-read-view--down-arrow,.react-datepicker__year-read-view:hover .react-datepicker__year-read-view--down-arrow{border-top-color:#b3b3b3}.react-datepicker__month-read-view--down-arrow,.react-datepicker__month-year-read-view--down-arrow,.react-datepicker__year-read-view--down-arrow{right:-16px;top:0;-webkit-transform:rotate(135deg);transform:rotate(135deg)}.react-datepicker__month-dropdown,.react-datepicker__month-year-dropdown,.react-datepicker__year-dropdown{background-color:#f0f0f0;border:1px solid #aeaeae;border-radius:.3rem;left:25%;position:absolute;text-align:center;top:30px;width:50%;z-index:1}.react-datepicker__month-dropdown:hover,.react-datepicker__month-year-dropdown:hover,.react-datepicker__year-dropdown:hover{cursor:pointer}.react-datepicker__month-dropdown--scrollable,.react-datepicker__month-year-dropdown--scrollable,.react-datepicker__year-dropdown--scrollable{height:150px;overflow-y:scroll}.react-datepicker__month-option,.react-datepicker__month-year-option,.react-datepicker__year-option{display:block;line-height:20px;margin-left:auto;margin-right:auto;width:100%}.react-datepicker__month-option:first-of-type,.react-datepicker__month-year-option:first-of-type,.react-datepicker__year-option:first-of-type{border-top-left-radius:.3rem;border-top-right-radius:.3rem}.react-datepicker__month-option:last-of-type,.react-datepicker__month-year-option:last-of-type,.react-datepicker__year-option:last-of-type{border-bottom-left-radius:.3rem;border-bottom-right-radius:.3rem;-webkit-user-select:none;-ms-user-select:none;user-select:none}.react-datepicker__month-option:hover,.react-datepicker__month-year-option:hover,.react-datepicker__year-option:hover{background-color:#ccc}.react-datepicker__month-option:hover .react-datepicker__navigation--years-upcoming,.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-upcoming,.react-datepicker__year-option:hover .react-datepicker__navigation--years-upcoming{border-bottom-color:#b3b3b3}.react-datepicker__month-option:hover .react-datepicker__navigation--years-previous,.react-datepicker__month-year-option:hover .react-datepicker__navigation--years-previous,.react-datepicker__year-option:hover .react-datepicker__navigation--years-previous{border-top-color:#b3b3b3}.react-datepicker__month-option--selected,.react-datepicker__month-year-option--selected,.react-datepicker__year-option--selected{left:15px;position:absolute}.react-datepicker__close-icon{background-color:transparent;border:0;cursor:pointer;display:table-cell;height:100%;outline:0;padding:0 6px 0 0;position:absolute;right:0;top:0;vertical-align:middle}.react-datepicker__close-icon:after{background-color:#216ba5;border-radius:50%;color:#fff;content:"×";cursor:pointer;display:table-cell;font-size:12px;height:16px;line-height:1;padding:2px;text-align:center;vertical-align:middle;width:16px}.react-datepicker__today-button{background:#f0f0f0;border-top:1px solid #aeaeae;clear:left;cursor:pointer;font-weight:700;padding:5px 0;text-align:center}.react-datepicker__portal{align-items:center;background-color:rgba(0,0,0,.8);display:flex;height:100vh;justify-content:center;left:0;position:fixed;top:0;width:100vw;z-index:2147483647}.react-datepicker__portal .react-datepicker__day,.react-datepicker__portal .react-datepicker__day-name,.react-datepicker__portal .react-datepicker__time-name{line-height:3rem;width:3rem}@media (max-height:550px),(max-width:400px){.react-datepicker__portal .react-datepicker__day,.react-datepicker__portal .react-datepicker__day-name,.react-datepicker__portal .react-datepicker__time-name{line-height:2rem;width:2rem}}.react-datepicker__portal .react-datepicker-time__header,.react-datepicker__portal .react-datepicker__current-month{font-size:1.44rem}.react-calendar{background:#fff;border:1px solid #a0a096;font-family:Arial,Helvetica,sans-serif;line-height:1.125em;max-width:100%;width:350px}.react-calendar--doubleView{width:700px}.react-calendar--doubleView .react-calendar__viewContainer{display:flex;margin:-.5em}.react-calendar--doubleView .react-calendar__viewContainer>*{margin:.5em;width:50%}.react-calendar,.react-calendar *,.react-calendar :after,.react-calendar :before{box-sizing:border-box}.react-calendar button{border:0;margin:0;outline:none}.react-calendar button:enabled:hover{cursor:pointer}.react-calendar__navigation{display:flex;height:44px;margin-bottom:1em}.react-calendar__navigation button{background:none;min-width:44px}.react-calendar__navigation button:disabled{background-color:#f0f0f0}.react-calendar__navigation button:enabled:focus,.react-calendar__navigation button:enabled:hover{background-color:#e6e6e6}.react-calendar__month-view__weekdays{font-size:.75em;font-weight:700;text-align:center;text-transform:uppercase}.react-calendar__month-view__weekdays__weekday{padding:.5em}.react-calendar__month-view__weekNumbers .react-calendar__tile{align-items:center;display:flex;font-size:.75em;font-weight:700;justify-content:center}.react-calendar__month-view__days__day--weekend{color:#d10000}.react-calendar__month-view__days__day--neighboringMonth{color:#757575}.react-calendar__century-view .react-calendar__tile,.react-calendar__decade-view .react-calendar__tile,.react-calendar__year-view .react-calendar__tile{padding:2em .5em}.react-calendar__tile{background:none;line-height:16px;max-width:100%;padding:10px 6.6667px;text-align:center}.react-calendar__tile:disabled{background-color:#f0f0f0}.react-calendar__tile:enabled:focus,.react-calendar__tile:enabled:hover{background-color:#e6e6e6}.react-calendar__tile--now{background:#ffff76}.react-calendar__tile--now:enabled:focus,.react-calendar__tile--now:enabled:hover{background:#ffffa9}.react-calendar__tile--hasActive{background:#76baff}.react-calendar__tile--hasActive:enabled:focus,.react-calendar__tile--hasActive:enabled:hover{background:#a9d4ff}.react-calendar__tile--active{background:#006edc;color:#fff}.react-calendar__tile--active:enabled:focus,.react-calendar__tile--active:enabled:hover{background:#1087ff}.react-calendar--selectRange .react-calendar__tile--hover{background-color:#e6e6e6}.react-datetime-picker{display:inline-flex;position:relative}.react-datetime-picker,.react-datetime-picker *,.react-datetime-picker :after,.react-datetime-picker :before{box-sizing:border-box}.react-datetime-picker--disabled{background-color:#f0f0f0;color:#6d6d6d}.react-datetime-picker__wrapper{border:thin solid gray;display:flex;flex-grow:1;flex-shrink:0}.react-datetime-picker__inputGroup{flex-grow:1;min-width:calc(16px + 3.674em);padding:0 2px}.react-datetime-picker__inputGroup__divider{padding:1px 0;white-space:pre}.react-datetime-picker__inputGroup__input{-moz-appearance:textfield;background:none;border:0;box-sizing:content-box;font:inherit;height:calc(100% - 2px);min-width:.54em;padding:1px;position:relative}.react-datetime-picker__inputGroup__input::-webkit-inner-spin-button,.react-datetime-picker__inputGroup__input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.react-datetime-picker__inputGroup__input:invalid{background:rgba(255,0,0,.1)}.react-datetime-picker__inputGroup__input--hasLeadingZero{margin-left:-.54em;padding-left:calc(1px + .54em)}.react-datetime-picker__inputGroup__amPm{-moz-appearance:menulist;font:inherit}.react-datetime-picker__button{background:transparent;border:0;padding:4px 6px}.react-datetime-picker__button:enabled{cursor:pointer}.react-datetime-picker__button:enabled:focus .react-datetime-picker__button__icon,.react-datetime-picker__button:enabled:hover .react-datetime-picker__button__icon{stroke:#0078d7}.react-datetime-picker__button:disabled .react-datetime-picker__button__icon{stroke:#6d6d6d}.react-datetime-picker__button svg{display:inherit}.react-datetime-picker__calendar,.react-datetime-picker__clock{left:0;position:absolute;top:100%;z-index:1}.react-datetime-picker__calendar--closed,.react-datetime-picker__clock--closed{display:none}.react-datetime-picker__calendar{max-width:100vw;width:350px}.react-datetime-picker__calendar .react-calendar{border-width:thin}.react-datetime-picker__clock{background-color:#fff;border:thin solid #a0a096;height:200px;max-width:100vw;padding:25px;width:200px} +/*# sourceMappingURL=main.22055773.css.map*/ \ No newline at end of file diff --git a/app/views/static/css/main.22055773.css.map b/app/views/static/css/main.22055773.css.map new file mode 100644 index 0000000..30fb01c --- /dev/null +++ b/app/views/static/css/main.22055773.css.map @@ -0,0 +1 @@ +{"version":3,"file":"static/css/main.22055773.css","mappings":"AAoGE,gBC7FF,CCPA,KAKE,kCAAmC,CACnC,iCAAkC,CAJlC,mIAKF,CAEA,KACE,uEAEF,CCZA;;;;;ECAA,OAQI,oPAIA,sMAIA,iKAIA,sNAGF,2BACA,qBACA,6BACA,6BAMA,yMACA,mGACA,4EAQA,gDACA,yBACA,0BACA,0BACA,wBAIA,kBCaF,kBC7CE,qBAeE,+CANJ,MAOM,sBAcN,OASE,6BACA,yCAFA,qBACA,CADA,kCACA,CAHA,aACA,CADA,0BACA,CALA,+KCmPI,CDnPJ,sCCmPI,eDjPJ,CCiPI,kCDjPJ,gBACA,CADA,sCACA,gBACA,CADA,sCACA,CALA,QACA,CAKA,oCAaF,CACE,GAEA,6BACA,SACA,CAHA,aACA,CAFA,aACA,CAGA,WAGF,gBACE,UAUF,2CAKE,eACA,iBAJA,mBAGA,CAJA,YASF,QCwMQ,gCAlKJ,2BDtCJ,OC+MQ,gBD1MR,SCmMQ,+BAlKJ,2BDjCJ,OC0MQ,cDrMR,SC8LQ,6BAlKJ,2BD5BJ,OCqMQ,iBDhMR,SCyLQ,+BAlKJ,2BDvBJ,OCgMQ,gBD3LR,SCgLM,iBD3KN,QC2KM,cD/JJ,GACA,mBADA,YDqBF,0CCNE,WACA,CAFA,yEACA,CACA,mEAMF,SAEE,iBACA,qBAFA,kBAQF,CDKA,MCHE,iBDSF,CCNA,SAIE,mBADA,YAIF,yBAIE,eAGF,CACE,kBAKF,CACE,sBACA,cAMF,YACE,eDGF,UCOE,kBAQF,cC4EM,gBDrEN,YAEE,yBADA,YAUF,SC0DM,eDtDJ,cACA,CAHA,iBCwDI,CDrDJ,uBAGF,KAAM,aACN,KAAM,SAMJ,gBACA,0BAEA,SACE,aAWF,6DAEE,aACA,qBDvBJ,mBCoCE,aACA,CAHA,sFCcI,CDdJ,oCCcI,cDZJ,CACA,0BAOF,KACE,aACA,CCEI,iBDDJ,kBACA,CAFA,YACA,CACA,aAKA,UAEE,aACA,CCRE,iBDOF,CACA,iBAIJ,MAGE,qBADA,aACA,CCfI,gBDkBJ,QACE,aAIJ,KAIE,wBE7SE,qBF4SF,UACA,CC3BI,gBD0BJ,CAFA,mBAMA,SC9BI,aDiCF,iBAFA,SAWJ,QACE,eAMF,SAEE,qBAQF,OAEE,yBADA,mBAIF,SAGE,aACA,CAFA,oBACA,CAFA,iBACA,CAEA,eAOF,CAEE,qBACA,gCDvDF,4BCkEE,eAFA,oBAUF,OACE,oBAMF,QAEE,eAQF,kCACE,SDrEF,uCCgFE,mBC9HI,kBDgIJ,qBAHA,QAOF,eAEE,mBAKF,eACE,cAGF,QAGE,gBAGA,iBACE,SAOJ,2CACE,YDjFF,iDC6FE,yBDvFF,6GC2FM,cAON,oBAEE,kBADA,SAMF,UACE,eAUF,UAIE,SADA,QACA,CAHA,WACA,UAUF,QACE,UACA,CCjNM,+BDsNN,qBAHA,mBCnNM,CDkNN,SACA,CAFA,UCnXE,2BDiXJ,OCxMQ,gBDiNN,WACE,UD/FJ,gPC6GE,SAGF,6BACE,WASF,eAEE,6BADA,mBAoBF,6BACE,uBAKF,gCACE,SAMF,wBACE,YAMF,8BAEE,0BADA,YAMF,QACE,oBAKF,QACE,QAOF,SAEE,eADA,iBASF,UACE,uBAQF,UACE,sBNnlBF,OOyQM,iBPvQJ,gBAKA,YOsQM,gCPpQJ,gBACA,gBOiGA,2BPpGF,WO6QM,cP7QN,aOsQM,gCPpQJ,gBACA,gBOiGA,2BPpGF,WO6QM,gBP7QN,aOsQM,gCPpQJ,gBACA,gBOiGA,2BPpGF,WO6QM,cP7QN,aOsQM,gCPpQJ,gBACA,gBOiGA,2BPpGF,WO6QM,gBP7QN,aOsQM,gCPpQJ,gBACA,gBOiGA,2BPpGF,WO6QM,cP7QN,aOsQM,gCPpQJ,gBACA,gBOiGA,2BPpGF,WO6QM,gBPvPR,EAKA,4BSzDE,gBADA,cT6DF,mBACE,oBAEA,oCACE,kBAUJ,aOsNM,gBPpNJ,yBAIF,aOgNM,kBP/MJ,kBAGA,yBACE,eAIJ,oBAIE,cOmMI,gBPnMJ,CAFA,kBOqMI,CPtMJ,gBAKA,2BACE,YC9FJ,CAMA,0BSCE,YAHA,cTiBF,CAfA,eAEE,qBACA,yBOGE,qBERF,CTGA,cAcF,SAEE,oBAGF,aAEE,cADA,mBAIF,iBAEE,cM6PI,gBI/RJ,oGCCA,iBADA,iBACA,CAFA,mBACA,CADA,sCACA,CAFA,oBACA,CADA,uCACA,CAFA,UC4DE,0BF5CE,yBACE,eE2CJ,2BF5CE,uCACE,eE2CJ,2BF5CE,qDACE,eE2CJ,4BF5CE,mEACE,gBE2CJ,4BF5CE,kFACE,gBGfN,OCAA,oBACA,gBACA,aACA,eAEA,CAEA,yCADA,yCACA,CAFA,sCDFE,QCaF,aACA,CAIA,8BAHA,cACA,CACA,wCACA,CAFA,yCACA,CAHA,UAmDI,MACE,QAGF,kBApCJ,aACA,WAcA,eACE,aACA,WAFF,eACE,aACA,UAFF,eACE,aACA,qBAFF,eACE,aACA,UAFF,eACE,aACA,UAFF,eACE,aACA,qBA+BE,WAhDJ,aACA,WAqDQ,QAhEN,aACA,kBA+DM,QAhEN,aACA,mBA+DM,QAhEN,aACA,UA+DM,QAhEN,aACA,mBA+DM,QAhEN,aACA,mBA+DM,QAhEN,aACA,UA+DM,QAhEN,aACA,mBA+DM,QAhEN,aACA,mBA+DM,QAhEN,aACA,UA+DM,SAhEN,aACA,mBA+DM,SAhEN,aACA,mBA+DM,SAhEN,aACA,WAuEQ,WAxDV,uBAwDU,WAxDV,wBAwDU,WAxDV,eAwDU,WAxDV,wBAwDU,WAxDV,wBAwDU,WAxDV,eAwDU,WAxDV,wBAwDU,WAxDV,wBAwDU,WAxDV,eAwDU,YAxDV,wBAwDU,YAxDV,wBAmEM,YAEE,eAGF,YAEE,eAPF,YAEE,qBAGF,YAEE,qBAPF,YAEE,oBAGF,YAEE,oBAPF,YAEE,kBAGF,YAEE,kBAPF,YAEE,oBAGF,YAEE,oBAPF,YAEE,kBAGF,YAEE,kBF1DN,0BEUE,QACE,QAGF,qBApCJ,aACA,WAcA,kBACE,aACA,WAFF,kBACE,aACA,UAFF,kBACE,aACA,qBAFF,kBACE,aACA,UAFF,kBACE,aACA,UAFF,kBACE,aACA,qBA+BE,cAhDJ,aACA,WAqDQ,WAhEN,aACA,kBA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,UA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,UA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,UA+DM,YAhEN,aACA,mBA+DM,YAhEN,aACA,mBA+DM,YAhEN,aACA,WAuEQ,cAxDV,aAwDU,cAxDV,uBAwDU,cAxDV,wBAwDU,cAxDV,eAwDU,cAxDV,wBAwDU,cAxDV,wBAwDU,cAxDV,eAwDU,cAxDV,wBAwDU,cAxDV,wBAwDU,cAxDV,eAwDU,eAxDV,wBAwDU,eAxDV,wBAmEM,kBAEE,eAGF,kBAEE,eAPF,kBAEE,qBAGF,kBAEE,qBAPF,kBAEE,oBAGF,kBAEE,oBAPF,kBAEE,kBAGF,kBAEE,kBAPF,kBAEE,oBAGF,kBAEE,oBAPF,kBAEE,kBAGF,kBAEE,kBF1DN,2BEUE,QACE,QAGF,qBApCJ,aACA,WAcA,kBACE,aACA,WAFF,kBACE,aACA,UAFF,kBACE,aACA,qBAFF,kBACE,aACA,UAFF,kBACE,aACA,UAFF,kBACE,aACA,qBA+BE,cAhDJ,aACA,WAqDQ,WAhEN,aACA,kBA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,UA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,UA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,UA+DM,YAhEN,aACA,mBA+DM,YAhEN,aACA,mBA+DM,YAhEN,aACA,WAuEQ,cAxDV,aAwDU,cAxDV,uBAwDU,cAxDV,wBAwDU,cAxDV,eAwDU,cAxDV,wBAwDU,cAxDV,wBAwDU,cAxDV,eAwDU,cAxDV,wBAwDU,cAxDV,wBAwDU,cAxDV,eAwDU,eAxDV,wBAwDU,eAxDV,wBAmEM,kBAEE,eAGF,kBAEE,eAPF,kBAEE,qBAGF,kBAEE,qBAPF,kBAEE,oBAGF,kBAEE,oBAPF,kBAEE,kBAGF,kBAEE,kBAPF,kBAEE,oBAGF,kBAEE,oBAPF,kBAEE,kBAGF,kBAEE,kBF1DN,2BEUE,QACE,QAGF,qBApCJ,aACA,WAcA,kBACE,aACA,WAFF,kBACE,aACA,UAFF,kBACE,aACA,qBAFF,kBACE,aACA,UAFF,kBACE,aACA,UAFF,kBACE,aACA,qBA+BE,cAhDJ,aACA,WAqDQ,WAhEN,aACA,kBA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,UA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,UA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,UA+DM,YAhEN,aACA,mBA+DM,YAhEN,aACA,mBA+DM,YAhEN,aACA,WAuEQ,cAxDV,aAwDU,cAxDV,uBAwDU,cAxDV,wBAwDU,cAxDV,eAwDU,cAxDV,wBAwDU,cAxDV,wBAwDU,cAxDV,eAwDU,cAxDV,wBAwDU,cAxDV,wBAwDU,cAxDV,eAwDU,eAxDV,wBAwDU,eAxDV,wBAmEM,kBAEE,eAGF,kBAEE,eAPF,kBAEE,qBAGF,kBAEE,qBAPF,kBAEE,oBAGF,kBAEE,oBAPF,kBAEE,kBAGF,kBAEE,kBAPF,kBAEE,oBAGF,kBAEE,oBAPF,kBAEE,kBAGF,kBAEE,kBF1DN,4BEUE,QACE,QAGF,qBApCJ,aACA,WAcA,kBACE,aACA,WAFF,kBACE,aACA,UAFF,kBACE,aACA,qBAFF,kBACE,aACA,UAFF,kBACE,aACA,UAFF,kBACE,aACA,qBA+BE,cAhDJ,aACA,WAqDQ,WAhEN,aACA,kBA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,UA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,UA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,mBA+DM,WAhEN,aACA,UA+DM,YAhEN,aACA,mBA+DM,YAhEN,aACA,mBA+DM,YAhEN,aACA,WAuEQ,cAxDV,aAwDU,cAxDV,uBAwDU,cAxDV,wBAwDU,cAxDV,eAwDU,cAxDV,wBAwDU,cAxDV,wBAwDU,cAxDV,eAwDU,cAxDV,wBAwDU,cAxDV,wBAwDU,cAxDV,eAwDU,eAxDV,wBAwDU,eAxDV,wBAmEM,kBAEE,eAGF,kBAEE,eAPF,kBAEE,qBAGF,kBAEE,qBAPF,kBAEE,oBAGF,kBAEE,oBAPF,kBAEE,kBAGF,kBAEE,kBAPF,kBAEE,oBAGF,kBAEE,oBAPF,kBAEE,kBAGF,kBAEE,kBF1DN,4BEUE,SACE,QAGF,sBApCJ,aACA,WAcA,mBACE,aACA,WAFF,mBACE,aACA,UAFF,mBACE,aACA,qBAFF,mBACE,aACA,UAFF,mBACE,aACA,UAFF,mBACE,aACA,qBA+BE,eAhDJ,aACA,WAqDQ,YAhEN,aACA,kBA+DM,YAhEN,aACA,mBA+DM,YAhEN,aACA,UA+DM,YAhEN,aACA,mBA+DM,YAhEN,aACA,mBA+DM,YAhEN,aACA,UA+DM,YAhEN,aACA,mBA+DM,YAhEN,aACA,mBA+DM,YAhEN,aACA,UA+DM,aAhEN,aACA,mBA+DM,aAhEN,aACA,mBA+DM,aAhEN,aACA,WAuEQ,eAxDV,aAwDU,eAxDV,uBAwDU,eAxDV,wBAwDU,eAxDV,eAwDU,eAxDV,wBAwDU,eAxDV,wBAwDU,eAxDV,eAwDU,eAxDV,wBAwDU,eAxDV,wBAwDU,eAxDV,eAwDU,gBAxDV,wBAwDU,gBAxDV,wBAmEM,oBAEE,eAGF,oBAEE,eAPF,oBAEE,qBAGF,oBAEE,qBAPF,oBAEE,oBAGF,oBAEE,oBAPF,oBAEE,kBAGF,oBAEE,kBAPF,oBAEE,oBAGF,oBAEE,oBAPF,oBAEE,kBAGF,oBAEE,kBCrHV,SACE,yBACA,iCACA,iCACA,sCACA,gCACA,oCACA,+BACA,qCAEA,CAIA,qBAFA,aACA,CAFA,kBACA,CACA,kBACA,CAJA,UAWA,0BAEE,mCACA,wBACA,yDAHA,aAMF,cACE,sBAGF,cACE,qBAIF,2BACE,oBASJ,cACE,gBAUA,6BACE,cAeF,iCACE,kBAGA,mCACE,kBAOJ,qCACE,qBAGF,sCACE,kBASF,4CACE,+CACA,oCAQJ,eACE,8CACA,mCAQA,+BACE,6CACA,kCC5HF,gBAME,qBACA,8BACA,8BACA,6BACA,6BACA,4BACA,4BAEA,CACA,qBADA,UAdF,kBAME,qBACA,8BACA,8BACA,6BACA,6BACA,4BACA,4BAEA,CACA,qBADA,UAdF,gBAME,qBACA,8BACA,8BACA,6BACA,6BACA,4BACA,4BAEA,CACA,qBADA,UAdF,aAME,qBACA,8BACA,8BACA,6BACA,6BACA,4BACA,4BAEA,CACA,qBADA,UAdF,gBAME,qBACA,8BACA,8BACA,6BACA,6BACA,4BACA,4BAEA,CACA,qBADA,UAdF,eAME,qBACA,8BACA,8BACA,6BACA,6BACA,4BACA,4BAEA,CACA,qBADA,UAdF,cAME,qBACA,8BACA,8BACA,6BACA,6BACA,4BACA,4BAEA,CACA,qBADA,UAdF,aAME,qBACA,8BACA,8BACA,6BACA,6BACA,4BACA,4BAEA,CACA,qBADA,UDqIA,mBAEE,iCADA,eH1EF,6BGyEA,qBAEE,iCADA,eH1EF,8BGyEA,qBAEE,iCADA,eH1EF,8BGyEA,qBAEE,iCADA,eH1EF,+BGyEA,qBAEE,iCADA,eH1EF,+BGyEA,sBAEE,iCADA,eElJN,cACE,mBASF,iBXuRM,iBWhRJ,iBAJA,eXoRI,CWrRJ,kCACA,CAFA,+BAUF,oBX4QM,kBW1QJ,gCX0QI,CW3QJ,6BAKF,oBXsQM,kBWpQJ,iCXoQI,CWrQJ,8BC5BF,YAKE,cZ4RI,gBY5RJ,CAJA,iBCDF,eAYE,uCZGE,CYLF,2BACA,CAFA,qBACA,CACA,wBACA,CZGE,oBaHE,CDJJ,aACA,CARA,aACA,Cb+RI,ca3RJ,gBACA,gBACA,CALA,sBb8RI,CcrRA,qEDVJ,UCcI,wCDhBN,cCiBQ,eDGN,2BACE,eAEA,yDACE,cAKJ,qBAEE,qBACA,qBACA,CAKE,6CARF,aACA,CAEA,SAYF,4CAEE,YAIF,0CACE,aAEA,UAQF,CAXA,oCACE,aAEA,UAQF,CAXA,2BACE,aAEA,UAQF,gDAEE,wBAGA,UAIF,qCAGE,0BE1EF,wBF6EE,CAGA,cACA,CAHA,oBACA,CAEA,2BACA,gBCtEE,CD+DF,aE3EF,CFyEE,uBACA,yBACA,CAHA,sBACA,CAIA,mBACA,CClEE,6HAIA,wCDuDJ,0CCtDM,uBDsDN,CCtDM,eDsDN,qCCtDM,eDqEN,2EACE,wBAGF,2CAGE,0BE7FF,wBFgGE,CAGA,cACA,CAHA,oBACA,CAEA,2BACA,gBCzFE,CDkFF,aE9FF,CF4FE,uBACA,yBACA,CAHA,sBACA,CAIA,mBACA,CCrFE,qIAIA,CAJA,6HAIA,wCD0EJ,0CCzEM,uBDwFN,CCxFM,eDwFN,iFACE,wBASJ,yBAOE,4BACA,CACA,4CAHA,aACA,CANA,aACA,CAGA,eACA,CAFA,eACA,CAFA,iBACA,CAFA,UASA,iFAGE,eADA,eAYJ,kBZ7HI,oBDkRE,iBClRF,CY8HF,oCACA,qBAIA,wCAGE,yBADA,qBACA,yBAFA,oBAKF,8CAGE,yBADA,qBACA,yBAFA,oBAMJ,kBZhJI,oBDkRE,iBClRF,CYiJF,mCACA,mBAIA,wCAGE,wBADA,mBACA,wBAFA,kBAKF,8CAGE,wBADA,mBACA,wBAFA,kBAUF,uBACE,qCAGF,0BACE,oCAGF,0BACE,mCAKJ,qBAEE,WACA,iBAFA,UAIA,oDACE,cAGF,wCZ9LE,qBY+LA,YAIF,2CZnME,qBYoMA,YGlNJ,cAKE,qChB2RI,CgB/PJ,wCAtBA,qBACA,+PAQA,wCFLI,CEKJ,4BFLI,yBACE,yBEMN,qBACE,CAbF,aACA,CAVA,aACA,ChB8RI,cgBxRJ,gBACA,gBACA,CAPA,sCAEA,CAkBE,oEAUF,CA/BA,UA+BA,wCAGE,aAGF,oCAEE,oBAKF,6CAEE,CAFF,SAEE,2DhBgPE,qBClRF,CewCF,oBfxCE,uBe8CJ,wBACE,6BAEA,iBhBiOI,0BClRF,iBgBTF,oBAFA,iBAEA,CAJA,qBACA,mBACA,CAJF,kBAME,iBASA,mBACA,CAFA,iBACA,CAPE,oBAIJ,kBAEE,CAPE,iBASF,aACA,aACA,CACA,qBACA,CAFA,oCAEA,+BACA,6BACA,mBAiBA,kBACE,CALF,uBACE,iBAbF,qBAGA,ChBXE,uBgBeF,CAJA,2BhBXE,CgBeF,uBAEE,iCAGF,CAbA,UACA,kBAaE,gCAGF,CAhBA,mBADA,SAkBE,kCAEA,kDAGF,iBACE,0BACA,8BAEA,uBAII,8CAIJ,4CAII,CARA,SAQA,mDAKN,+DACE,4PAqBE,uCAiBJ,oKhB1FE,CaHE,uEAIA,CGyFJ,sPAkBM,CH3GF,oBG2GE,CAMR,2BAEE,mBAGF,YACE,WACA,CANA,mBAMA,4FAMI,wBACA,kDC1IJ,iLAS4B,sBAC1B,mBAXF,kBACA,CAUE,+CAA0B,CAZ5B,SAY4B,wCAG5B,+BAIA,sDAGE,uKJbE,CImBF,uCJnBE,oKAKE,CALF,wBAKE,oBIgBJ,kDAKF,uCHtCA,iBGsCA,oDAKE,mBACA,wBjB7BA,CiB2BA,mBjB3BA,acfF,uBdeE,iBiBmCA,4BHlDF,CGiDA,uBACE,CADF,UjBlCE,mBaHE,6MI6CF,+BJzCE,2CI8DF,uCACE,CApBF,oDH3DF,CDiBM,WI0CJ,CJ1CI,kBI0CJ,CH3DF,8GGqEE,uGASA,CA7BF,UA8BI,wCAGF,kCACE,uBCvFN,CACE,iBAEA,iEAEE,4CASA,wBACA,yBACA,mBACA,CAPA,iBAEA,eAEA,CALF,YACE,CADF,UAQE,+BARF,gBLOI,qDKCF,WLDE,mNKPJ,CAQE,UARF,wCAaA,8BAGE,kIACE,wBAGF,6CAJA,gCACE,CADF,aACE,UAGF,sBAEE,mBACA,4CAHF,gEAEE,wBACA,gBAGF,2EAEE,yBAIJ,iBACE,sBAQE,4BACA,CAFF,0DACE,CAPF,iBAMA,OAEE,iDAFF,kIACE,CACA,sIAKF,mEAEE,iJCrDJ,iBAEA,2CAIA,2EAKE,sBAIF,CANE,oBAMF,yFAYE,uBAHA,oBAGA,+CAeF,sBpBsPI,CoBvPJ,oBpBuPI,6BoBlPJ,sBACA,CAFA,oBAEA,CACA,+DAEA,WnBpCE,2DHorFJ,uIASA,yIE36EM,qDoBlNN,8EtByoFA,qJGzrFI,sDH8rFJ,mKG9rFI,WACA,iCmBgFF,sCnBnEE,wBACA,yBoBxBA,qBACA,CDyFF,iGACE,iBnBpEA,mBoBtBA,mHAcA,mBrB4PE,CqB7PF,kBAFA,kBrB+PE,mHF89EN,qHuB7vFI,mBA0DI,iUAMA,4BAhEJ,CA+DI,yBA/DJ,uKA0EI,yCA1EJ,iBAoFI,cApFJ,aAoFI,iBAHF,iBAGE,CAHF,UAGE,mDAEE,sBAFF,mIAGE,+HACA,wEAKJ,wQAWA,CACE,wDAGF,CAJA,2BACE,CAGF,2DACE,CApBE,uDAoBF,uEAGF,gEAMF,2EAtHA,yKAmIE,iPrB4JA,sfqBvOE,6DACA,CACA,qEACA,CArCJ,sBAqCI,qEAGF,gEACE,mEA/DJ,uGA0EI,uGA1EJ,2CAiFE,uGAGE,0PAGE,SACA,+LAKJ,SACE,mBA9FJ,cA8FI,YACA,iBA/FJ,CA+FI,4BA/FJ,kBAwGE,mCAIA,sBAJA,WAFA,YAEA,wEAxGF,0BAsGE,UAMA,+IAIA,4EAOA,qUAaE,CCrIN,wDAKA,CDgIM,2BCrIN,CAKA,2DAIA,CD8GE,uDC9GF,2EAEA,gEC+GA,+ET/GI,6EAIA,CAJA,kCAIA,6DQEF,oBAIF,qOCDA,mjBAwCA,6DAME,sEAjDF,gGAME,oBAGF,6CAEE,sEAOE,2GAIJ,2GAME,2CAKA,gLAKI,+LAtCN,UAGA,sMAeI,eAaF,4BAEA,mDAXF,6BAJI,oBAIJ,CAWE,eAXF,gCAWE,uBAXF,uCAWE,6HAUF,CArBA,wBAME,qBAGA,kBATF,qBAqBA,wCAGE,oBAGA,aDZF,aCvCA,kCAEA,4CRlBA,CQkBA,SRlBA,oDQ2BA,+BAEE,cR7BF,wBQ+BE,qBAKE,CRpCJ,UQoCI,CAIJ,kHAJI,UAeF,CAXF,iDAME,2CAKA,2IAKI,6CAKN,CALM,UAKN,yKAxCA,2CAGE,6CAGF,wBR3BA,qBQ+BE,CAJF,UAIE,gBAKE,6CAIJ,CATE,UASF,mIAWE,CAXF,qDAWE,gMAKI,wBAKN,sBALM,UAKN,mLR7DA,2CQ2BA,iDR3BA,wBQ+BE,CAKE,qBRpCJ,UQoCI,cAIJ,8CAJI,UAIJ,6HAWE,CAXF,iDAWE,sLAUF,wBAEE,qBACA,CAHF,UAGE,yKArCF,wFAIE,wBAKE,sBALF,UAKE,WAIJ,yDAKE,2DALF,6CAKE,CALF,UAWE,CANA,2CAMA,uKAUF,wBAGE,sBAHF,UAGE,0JAxCA,2CAGF,uCAIE,wBAKE,sBRpCJ,UQoCI,cAIJ,yDAKE,oEALF,6CAKE,CALF,UAWE,CANA,iDAMA,qLAaA,wBAGA,qBDNF,CCEE,UDFF,yKC4BA,0CAEE,6CAGF,8HAOE,iEAPF,wBAME,sBANF,UASE,CAFA,+CAEA,gLAUF,6CAEE,CAFF,UAEE,oKA7BA,0CAGF,2CAEE,6CAGF,CALA,UAKA,2LASE,CATF,6CASE,oMAKI,sBALJ,UAKI,+JA5BN,2CAGA,yCAGE,wBAGF,sBAJE,UAIF,mCAEE,sBAFF,UAEE,CAGF,mHAME,CANF,oFAME,4HAGA,kNAUF,yCAGE,uCAnCF,wBAEA,qBACE,CAJF,UAIE,CACA,qBACA,oBAGF,CAJE,aAIF,oDAEE,sBAFF,UAEE,kEAGF,6NASE,wQAaA,2CApCF,6DAKE,4BACA,CAFA,aAEA,wBAGF,gEAEE,wBAGF,sBAHE,UAGF,6SASE,kRD1CF,2CCsBA,iEAMA,6BAHE,aAGF,sBAEE,qBAFF,aAEE,4BAGF,oKAKE,kLAIA,wQAtBF,0CAGE,6DAKF,6DAEE,2DAGF,+JAME,mKAGA,yPAvBF,2CAGA,uDAMA,6BAHE,aAGF,sBAEE,qBAFF,aAEE,4BAGF,oKAME,kLAGA,6CAKI,CALJ,UAKI,gND9BN,0CASA,6DCoCA,4BtB5GE,CsB4GF,atB5GE,qBqByFJ,oBCmBE,CDnBF,aCmBE,2BtB5GE,wBaHE,qBAIA,CbDF,UaCE,gEUjBJ,0CAOA,6KAWE,wBVLE,qBAIA,CUAJ,UVAI,2MWWA,0CAEA,2DA9BJ,4BACA,CADA,aACA,oBACA,oBAqDE,CArDF,aAqDE,0BC1CJ,wBAEE,qBACA,CDwCI,UCxCJ,8DAKA,4CAEA,CACA,uKAqBE,wBACE,qBAEA,CAfF,UAeE,sMAFA,4CAEA,CACE,wDAQF,4BACE,CAHF,aAGE,mBpBEJ,oBoBfA,CpBeA,aoBfA,yBAGE,6CACE,CAHF,UAGE,4DAQF,yCAEE,mKAFF,6CAEE,CAJF,UAIE,iMAFF,yCpBGF,uDoBdE,4BAEA,CAFA,aAEA,WAEE,aAIJ,CANE,eACE,CAKJ,yBACE,iBAEA,oDAYJ,yCAIE,mBD9CA,CC6CA,iBACA,CAHA,kBD3CA,4BAEE,mBACA,CADA,kBAFF,oBAGE,OACA,8BAxBJ,wCACA,qBACA,4BA8CE,+CACE,QC0BJ,4CAEE,wCAGA,YD5DA,iDAEE,sCAEA,CAHA,OAGA,wCAlBJ,gCAEA,uDAwCE,mCACE,kBCoCF,wBDjEA,kDAEE,CC0EF,mCD5EA,CC0EA,qBAEA,CAHA,UACA,CATA,oBAOF,yCDtEI,8BACA,aACA,gBAXJ,2BACA,CADA,sBACA,gCACA,qBAiCE,CAPI,aACA,CANA,YAGF,CACE,cACA,CACA,eA7BN,CA2BM,SADF,+BACE,CALF,kBAOE,gBAPF,YAaF,gCACE,MCqDF,oBDtDA,QCsDA,sBACE,mBAON,sCAGE,MACA,CADA,UACA,qCAMF,oCAGE,2CAEA,wBAEA,mBACA,yCAEA,wCAeA,wDXvJA,SW8JA,CX9JA,OW8JA,mDAEE,mBACA,yCAIF,wCAEE,iBACA,uCAOJ,UANI,OAMJ,2BAKA,wBAEE,mBACA,yCAGA,OADA,UACA,uBAIF,iBACE,uCAMF,mBACE,0BACA,wBACA,mBAGA,yCAGE,OAFA,UAEA,+EX9MF,UWgNI,OXhNJ,4BWoNE,sFXpNF,OWsNI,UXtNJ,wBW0NE,mHAMF,WACE,sBAGF,CAHE,aADF,QAIA,gCCpOA,wBACA,mC7B0uHF,C6B9uHA,mCAGE,CAHF,wBDwOI,oBAGF,yC5BmgHF,sC6BtuHI,a7BgvHJ,8W6BruHI,iCAOF,gBACA,2C7BgvHF,4C6B7uHI,U7B6uHJ,C6B9uHE,K7B8uHF,mCAIA,W6BzuHI,oB7ByuHJ,aG7uHI,CH6uHJ,yCG7uHI,mCAcA,oCACA,CHouHJ,uBGruHI,CHquHJ,6CGlvHI,oBHkvHJ,0CGpuHI,yC0BsBF,aACA,oCAEA,uEAMA,CANA,uCAMA,gBAUF,sCALA,yBALE,cAKF,gBAJI,mBAIJ,mBACE,qBACA,oBAPA,UAUF,2CAEE,wBAoBF,CArBE,aAqBF,6CAGE,wBAEA,CAHA,+BAGA,iD7BosHF,6B6BlsHI,a7BksHJ,sD6B7rHI,kB7BisHJ,wHGnxHI,cADA,iCACA,qBHwxHJ,qDGvyHI,CHuyHJ,aGvyHI,oCACA,a2BxBJ,mFASE,oCAIA,CALF,UAKE,qFdFI,4HcMJ,cAEE,sCAMA,4BAEA,yCASF,mDAGE,aACA,gC3BjBA,mBACA,C2BiBA,iB3BlBA,CACA,+D2BsBE,cAFF,iBAEE,mXAiCJ,uBACE,Y3BlEA,0C2BuEF,qCAEE,2EAaA,gBACA,oGAeF,6BANE,yBAMF,8GCvGA,2BAEA,CAHA,wBAGA,wBAEA,qBAEA,CAHA,sBAGA,yGAOA,qEACE,CACA,yEAuBF,oBACA,CAFF,qBAEE,0EAiBF,mBAEE,CAfA,oBAeA,qBAEA,sBACA,CAFA,qBACA,CACA,sBAEA,yDAKA,UACE,6FAyBF,eAGA,sH5BhGE,2BaHE,Ce4GJ,4Bf5GI,qFeuGN,wBASE,0BACE,kBAGF,eACE,CAEA,gBAFA,eAEA,CAFA,cAEA,WAOF,aACA,CAFF,gCACE,CACA,oBAEA,kGAMF,wCACE,2DvBzFE,cuBsGA,mBAEI,cACA,cAEA,CAHA,mBAGA,0CACE,CAEA,kCACE,6BAGF,8BACE,+BAKJ,CAVE,kBAUF,qDAIA,oCACE,gDAIF,4BAIA,0BAJA,aAIA,+DAOE,qBACA,CACA,iCACA,CALA,aAKA,0BAEA,wBfhMJ,0BekMI,CAHA,eAGA,C/B41HV,yH+Bn1HQ,yBAHE,UAGF,yCAIE,+BvBhKN,mDuByGI,wBAEA,oFAGE,UACE,wBAGF,kCACE,sBAKJ,kBACE,CADF,4BACE,6BAGF,CACE,oBACA,CAFF,iBACE,CATE,iBAUF,4JAgBA,mBAHA,YACA,kBAEA,8BACA,eAIA,iB/Bi5HV,C+Bj5HU,iB/Bi5HV,C+Bn5HU,uBAEA,CAHA,oBACA,C/Bm5HV,sF+B54HU,CAIF,gBAHE,eAGF,CAJE,cAIF,uBAGE,cACA,CAFA,eAEA,4BvBhKN,euBsGA,cAGI,qBADA,iBACA,CAEA,iBACE,kBAEA,CAHF,eACE,YAEA,iBAIA,4BACE,6BAKJ,sBATI,iBAGF,eAJA,qBACE,CASJ,sCAIA,wCACE,gBACA,eAGF,4CAIA,uBAIA,wBAHE,SAGF,CAJA,oBAIA,sBAOE,uBf/LJ,Ce8LI,2BACA,Cf/LJ,oBekMI,CARA,oBAEA,CACA,kCACA,CAFA,WAMA,mC/Bs8HV,kF+Bj8HU,kBACA,gBAGF,2BACE,+BAGA,kBvBhKN,8CuBwGI,iBACA,yCAGE,kBAEA,CAHF,mBAGE,sDACE,oCAGF,sBACE,gBACA,CAKF,sEAGF,0CASA,4BAIA,cACE,CADF,eARA,oBACE,CAJA,gBAGF,CAUE,sBAEA,eACA,CAJA,eACA,CATA,4BAGF,CAJA,YAaE,sEf7LJ,eekMI,CAFA,YfhMJ,CegMI,WAEA,mC/B2/HV,gF+Bt/HU,kBACA,gBAGF,2BACE,+BAGA,kBvBhKN,8CuBsGA,iBAGI,yCAGE,kBAEA,CAHF,mBAGE,sDACE,oCAGF,sBACE,gBACA,CAIJ,sEAIA,0CASA,4BACE,CAGF,cAHE,cAGF,CAXE,QAGF,aAJE,gBACA,CAYA,sBAEA,eACA,CAJF,eACE,CATF,4BAIA,CAJA,YAYE,sEf5LJ,eekMI,CAFA,YfhMJ,Ce+LI,WAGA,+C/BgjIV,oE+B5iIU,kBAEA,gBAGF,0DAEE,kBAEA,8CAvDF,0DAKE,mBAFA,mBAEA,sCAIA,oDACE,sBACA,CAIJ,gBAIA,sEACE,YACA,8BAWF,4BACE,CAEA,aACA,CAHA,cAEA,CAVA,QAGF,aAJA,gBACE,CAYA,uBACA,eAFA,eACA,CATF,4BAIA,CAJA,YAUE,sEAIA,e/BomIV,C+BpmIU,wB/BomIV,2D+BhmIU,CACA,kBACA,CAFA,SAEA,4BAGF,kBACE,gBAEA,2BAeR,+BACE,kBAEA,wGAOA,kBACE,CADF,mBACE,sCAEA,oDAEE,sBAGF,iB/BmlIN,gH+BtkII,4BAGF,8BATI,SAIJ,Y/BwkIF,gB+B5kIM,CAUF,sCADF,eACE,CANF,4BACE,CALE,YAUF,sEAMA,wFAGE,YAOJ,mBAPI,SAOJ,4BAGE,8DAEE,gCAKF,kBACE,+CAEA,2DAEE,kBAGF,CAHE,mBAGF,uCACE,iB/BkkIR,2E+BxjIE,wEAEE,2CAOF,4BACE,cACA,CADA,eAJA,qBADF,iBAME,sDALA,4BAGF,CAHE,YAKA,wECjUF,eACA,CADA,aADA,WAEA,oCAGA,YACA,+BACA,CADA,SACA,iC7BME,2B6BDA,4BAIF,kBACE,2CAGA,kBACE,qC7BCF,kBACA,CADA,mBACA,mC6BEA,iD7BWA,uCACA,gE6BJF,uCAeA,4BAIA,CACA,aAGF,CAJE,cACA,CAdF,QAGE,CACA,WAIF,CAZI,gBAIJ,CAkBE,sBAQA,gBATF,gBATA,4BACE,CANA,YAuBA,CACE,+DAYF,eACA,CADA,aAFA,WAGA,4CAEA,a7BpEE,mB6BoEF,S7BpEE,C6B0EF,gGAGA,oBAEA,qC7B/EE,0G6B4FF,oBACA,8CAYA,oBAIF,sF7B7GI,mD6B0HF,2BAGF,CANA,qBAMA,oC7BpHI,sQ6B+IA,4BAGE,qBAEA,oG7B7IF,oBACA,CH4+IJ,uGgCn1IY,oChCu1IZ,4GgCl1IY,yBAIJ,6C7BpJJ,yBH0+IJ,8FgCh1IY,8BhCo1IZ,4FgC/0IY,4QhBjMN,qJiBZN,iBAiBI,oBACA,2BAEA,CAFA,sBAEA,kCACE,qBANJ,kCACE,YACA,CjBAI,iBiBIF,uBACA,CADA,cACA,2DAKJ,+BAEE,yCAGA,2CACA,CALA,kBAKA,8BjBrBE,6CiBuBF,6CjBvBE,CiBsBF,qBjBtBE,uFAIA,uCiBWJ,mBjBVM,gBiBsBN,kBAIA,sCAJA,eAME,uBACA,gBACA,cAIJ,gCAIA,yCAEE,CANF,gBAJI,kBAUF,0BAEA,uD9BlCE,c8BqCA,sE9BtCA,CACA,kBADA,yBACA,uD8B0CF,mB9B7BE,eACA,C8BiCF,oB9BjCE,sCADA,mB8B8BA,mBAQE,mB9BrCF,iCADA,4BACA,C8BqCE,iB9BtCF,cACA,0C8B0CA,mC9B3CA,yCACA,2C8B0DF,4BAIA,4C9BtFE,C8BkFF,6C9BlFE,mB8B2FA,8CAAgB,YAChB,kDAEA,iD9B9FA,a+BnBJ,CDiHI,aCjHJ,oCAME,4BAOA,CAVA,yBAUA,kGAKI,yBACA,qGCnBN,4B/BIE,qC+BGA,2BAEA,CAHA,wBAGA,oGnBOI,+HmBXN,2BASE,oBAIE,kBACA,CAMA,qBAEA,0BAHA,aACA,CATA,YAEA,CAIF,cAEE,CAGA,oBAKF,CAbE,oBAGF,CAPE,iBACA,CASA,gBAOF,qJAWA,CAxBE,UAwBF,wCACE,kBACA,eACA,oCCzCA,wBAOI,2CjCqCJ,CiC7CF,ajC6CE,yCACA,8SiChCI,kCjCiBJ,0BACA,yBiCxBI,8SCDN,4BAEA,wBACA,CDFM,WARN,cACE,clCgSE,iBkCzRE,CCEN,4CAKA,qCAOA,uECnBA,CFHA,aEGA,wCnCYE,wBmCNJ,eAEE,0BAKA,UAQF,wBACE,oBAGA,6CAIE,CAJF,UAHA,SAOE,CACA,iCAeF,iBrBhDA,uDsBEA,+BACE,6BALF,+BAEA,iDAGE,yCtBHF,2CsBAA,qCAGE,YD6CF,8BChDA,gCAGE,CtBHF,iCsBGE,0DADF,4CAJA,CtBEA,6CsBFA,kDAKE,gCALF,CAEA,iCAFA,iBtBEA,oBsBAA,sCAGE,cD6CF,kCChDA,6BAEA,CtBFA,csBEA,8CCFA,4HAMF,eACE,aACA,YACA,etCwRI,CClRF,gBqCJF,mBtCsRI,SClRF,mCqCGF,4DAKA,yBACA,yCxBRI,CwBKJ,UACC,CAAD,mBxBNI,uCwBAN,wBxBCQ,CwBWR,evBYE,CDvBM,cCuBN,2PuBLE,qFADF,oBAKM,CADF,SACE,mCADF,wBAJJ,CAII,arCvBF,CqCwBI,iBClCN,4CtCUE,CsCbF,SAGA,CDkCM,SrCxBJ,yCsCJF,gBACA,8BAIE,6CACA,CAHF,UAEE,CAFF,SAGE,gCAaF,qBAGA,sBANF,aACE,CACA,mBAIA,kCAEE,mCAGA,gCAGF,CAJE,6BAIF,kCAYA,iCAEA,CAZE,8BAYF,2BAGA,kBADA,qBACA,CACA,iDtCnCE,+BACA,CsCoCF,4BtCpCE,iDAaA,gCACA,CADA,6BACA,2BsC8BF,iBAEE,CAFF,oBAEE,kDAOA,+BAEA,CAHF,4BAGE,iDAKA,gCAEA,CAHF,6BAGE,QtCjBA,oBAZA,CsCiDI,WApBJ,oBAEE,CAcF,+BACE,cAGE,CAlBF,mBAcF,CAII,kBtCrCJ,wBsCqCI,kBtCjDJ,csCsDI,mDtCtDJ,QsC2DI,iDACE,CtChDN,kBsC+CI,CtC3DJ,YAYA,CAZA,iBsC4DM,gBAGF,6DACE,kBACA,+BAEA,oBAEE,CAFF,yCAEE,gBjCpER,wBiC4CA,qBACE,CAuBM,aAvBN,4BAGE,+BtCrCJ,6CAZA,CsCiDI,atCjDJ,8BsCsDI,6BtCtDJ,wBAYA,sBsC0CI,atC1CJ,4BsC+CI,0BAIA,8CAJA,aAIA,yBACE,aACA,gBAEA,8CAFA,aAEA,4BAEE,4BjCpER,wBiC4CA,qBACE,CjC7CF,aiC6CE,2BAGE,2BtCrCJ,yBAZA,qBsCiDI,atCjDJ,0BsCsDI,0BtCtDJ,yBAYA,qBsC0CI,atC1CJ,yBsC+CI,sDAIA,+DACE,6BACA,CAEA,WACE,wBACA,qBjCpER,CiCkEM,gBACE,CADF,WjClEN,yBiCkEM,4BAbF,CjCrDJ,cLWA,wBAZA,CsCiDI,WAJJ,qBACE,uBAGE,mBtCjDJ,yBsCsDI,CALA,kBAKA,sDtCtDJ,uCAYA,qKsCuDM,2GjClEN,kDiC6CE,wCAGE,6CtCrCJ,6BsC0CI,oBtCtDJ,mCsCsDI,+BtCtDJ,sBAYA,qBsC+CI,CtC/CJ,oBsC+CI,gCACE,kCAGF,mDAEE,gCAEA,CAHA,UAGA,6DjClEN,wBiC4CA,CAwBQ,kCjCpER,CiCmEQ,SAvBR,gCAII,wDAKA,uDtCtDJ,csCsDI,CtC1CJ,gCAZA,CsCiDI,iBtCrCJ,CsC0CI,oBtCtDJ,8BAYA,8BsC+CI,gCACE,6BAGF,iCAEE,CAFF,kCAEE,qDAGE,qBACA,CAFF,iCAEE,yBAiBV,6CACE,CtClIA,UsCiIF,CAHF,SAII,mCAEA,4DClJA,oBACA,CAFF,eAEE,wBAGE,uGAEE,0BACA,oDAKA,2BACA,CAHF,8BAGE,gDAZJ,sEAGE,yGAMA,gEACE,0BACA,kBACA,wDAZJ,gCAGE,iFAGE,2BAGF,CANA,8BAMA,mDACE,YACA,6DAXJ,mBAGE,CAJF,oBAIE,yFAEE,CAFF,gBAEE,2BACA,0BAGF,0EAGE,gCAdN,0BAEE,uDAGE,6GAGE,YAGF,6DAEE,mBACA,CADA,oBACA,oEATF,6GAEE,wDAIF,iCACE,yBACA,uDAXJ,2BAGE,CAJF,8BAIE,4HAMA,4GAXJ,qBAEE,CAYI,gBAZJ,4BAGE,oGAEE,gCACA,0BAGF,uDAGE,2BCZN,CDWM,8BCXN,mDAIA,YACA,gFxCQE,CwCRF,oBxCQE,oEwCCA,qBAGF,CAJE,gBAIF,CACE,2BACA,2BACA,kBAGF,yDAGE,0DACA,wDCjCJ,2BDsCE,mFCpCA,Y1CmSI,8D0C9RJ,mBACA,CAFA,oBAEA,qEzCWE,qByCNA,CzCMA,gByCNA,oBAGF,eACE,oCAKF,mEAIA,+CAKF,wBAEE,CANE,aAMF,6GAKA,yBADA,aACA,wDzCTE,6CyCYF,CzCZE,UyCYF,4BAEE,wBAKF,CALE,aAKF,iHCjCA,wBAIA,CAJA,aAIA,0DAYA,wBAGA,qB7BlBI,C6BaJ,U7BbI,6K6BoBF,wB7BhBE,sEACE,wB6BiBN,qBACE,CALF,UAKE,uBAIF,wBACE,CALA,aAKA,uGAOF,sCACE,qDAIF,wBAKF,qBACE,CANA,UAMA,0BAEA,wBAIF,CALE,aAKF,6GASE,wBACA,CAFA,aAEA,wDAQF,wBCnFE,qBAEA,CD6EA,UC7EA,yBAGA,wBAGA,CAJA,aAIA,2GDqFA,wBACA,CAFA,aAEA,uD1CrEE,wBACA,sBADA,UACA,wB0CwEF,wBACE,CADF,aACE,CACA,wGAgBF,wBAKF,CARE,aAQF,sDAIE,wBACA,qBAEA,CAHA,UAGA,uB1CzFE,yB0CyFF,a1CzFE,uG0CuGF,wBAEE,CrCpFA,aqCoFA,qDAIA,wBAGF,qBACE,CAJA,UAIA,YAWF,+WrC1FE,CqC0GA,6BACE,CA5BF,sBAOU,CAIZ,WrCvGE,WqCwHE,UACA,CrCzHF,aqCuGF,CAJY,SAsBR,kBACA,UACA,CAEA,YAFA,oBAEA,kBAEE,4CAIF,WALE,SAKF,yC1C/KF,WKyDA,CqC0HE,4CAIA,sCrC9HF,2EqC4GE,kDAIA,QAUA,4BAJA,oCAIA,gCAIA,C1CvLF,oBKyDA,CqC8HE,uC1CvLF,C0C2KI,iB1C3KJ,C0C0KI,cACA,C1C3KJ,mB0C+KE,CANA,WrChHF,yBqC0GA,+BAEE,kBAIA,kCAEE,CALF,yBAGA,kBAEE,oCAIF,mCAIA,mB1CnLF,2BKyDA,CqC8HE,oC1CvLF,CKyDA,uCqC2GE,0CAGA,2CAEA,CAWE,aAGF,C1CvLF,Y0CmLE,qBAVA,CACE,yBAKF,mB1C/KF,qB0C+KE,aAIA,qB1CnLF,c0CmLE,QAIA,wB1CvLF,C0CoLI,MAGF,CrC9HF,SqC0GA,C1CnKA,iBKyDA,iBqC0HE,cACE,OAGF,uBApBF,eAEE,YACA,CACA,mBAEA,CANF,iBAEE,WAIA,2BAWE,mCAGF,4B1CvLF,C0CyKE,yC1CzKF,C0C+KE,kG1C/KF,wC4ChBF,0BAEA,eCJA,kPAGA,eACA,gBACA,sCAEA,eACA,wBAEA,kBACA,CAFA,YACA,CACA,4BACA,gBDGA,2BACE,CAHF,qBAEA,CACE,+BAEA,oBAGA,CCPF,Y9CsRI,sB6C1RJ,CAWE,UAXF,mBAGA,CCAA,iBACA,CDJA,UAWE,iBAIE,qBAKN,CANM,YACA,CAFA,OAFF,cACE,MACA,CACA,YADA,YAON,qDACE,yBAEA,mBAGE,kHAEE,CALJ,qEAKI,0BAMN,iCALM,aAKN,cACE,gBADF,eAGE,wEAGE,kBAEA,CAEE,2CACA,CAHF,4CAEE,CAFF,6BALF,aAEE,aACA,CAHF,cACE,CAIA,uCAGE,iBAKN,qDACE,mBAEA,CAHF,eAGE,0GAGE,oHAEE,0BACA,oBAKN,oCALM,WAKN,kCAGE,yBAFA,WAEA,+EAGE,eAEA,uGAEE,qBADA,cACA,CAFF,WAEE,CACA,yCAsBJ,QACA,gBACA,CAFA,WAEA,yCAEA,e5C7FE,uC8ClBF,eAGA,yCAEA,eDLA,8NAEA,eACA,uCAEA,eACA,yCAEA,eACA,8BACA,0BAGA,WACA,UAFA,cACA,CADA,WAEA,0CCFA,wBACA,CADA,WACA,yCACA,e9CIE,uC8CCA,eACA,yCAIA,wEAIE,WACA,UAFA,cACA,CAFA,WAGA,0CAOJ,6EACE,sDAEA,sGAEE,2BAIF,qBAHE,cAGF,CAHE,WAGF,mEACE,CADF,WACE,0CAEA,eAMJ,iGACE,0BAsBA,qBAlBA,6LAMA,uCAYA,kBAZA,iCAEE,CAOJ,sBAGE,gBAVE,gBARF,SAkBA,UArBA,iBAGA,CAQE,gBACA,qCAMJ,sCACE,kBAEA,CAHF,qCACE,CAhBA,YAkBA,kDAEE,aACA,cADA,kBACA,2CAGF,sIAGE,2GAKJ,kHAaA,sBALE,0BAKF,CALE,QAKF,+EACE,8FAIA,2IAMA,uIAEE,eACA,kGA0BJ,KACA,gHAIE,wBAKF,CANA,0BACE,C9CzHA,W8C8HF,iEC1IF,eACE,+FCdE,YACA,CAHF,OACE,YAEA,6GD6BF,sBlClBI,CkCkBJ,0ClClBI,2GAIA,iBAJA,UhBy3LN,0HkDj0LI,CA5CJ,alCPQ,ChB61LR,+KkDn0LE,sClD+0LF,CAWA,kBAXA,kCgB12LM,OkC2CJ,qBAEE,CACA,elDu0LJ,CAXA,gBgBz2LQ,ehB61LR,CgBl2LM,iBAIA,ChB02LN,ekD10LE,uDAWA,qBlC3CI,MkC8CF,mBADA,qCACA,ClC9CE,YkCqDF,yBlDs0LJ,qHAOA,4CgBl4LM,UhBk4LN,CgBl4LM,+BhBk4LN,4FAMA,yBkDh0LE,0GAYA,gClCxFI,CkCsFJ,0BAEA,CAFA,QlCtFI,wGhBq6LN,iDgBh6LQ,UhBg6LR,8FkDh0LA,YALI,uBAEA,YAGJ,4GAeE,kCACA,CAHA,gCAEA,CAHA,MAIA,0GAgBA,wBAHA,gCAGA,CAHA,QAGA,kGAaA,sBAEA,gHASE,mCAGA,CAJF,0BACE,CADF,KAIE,8GAUA,yBAHA,0BAGA,CAHA,OAGA,kHAOA,+BAUF,CAXA,UACE,CArBF,alCvJM,CkCuJN,SAoBA,mBlC5KI,iBkCwJJ,OlCvJM,UkCsLN,+FlDw0LF,YkDl0LE,wBlDk0LF,yHkDtzLE,kCAHE,gCAGF,CAHE,OAGF,2GEvNK,sBADP,CACO,0CADP,iBACO,wBAIP,uCAGE,yCAEA,0CACA,CAVK,8BAIP,CAJO,kBAUL,uBAEA,YACA,8FAGF,kBACE,iBAUF,gBATE,iBACA,WAQF,uBAEI,qBAEF,CAFE,aAEF,gBAFE,mCAEF,0BAEE,CANJ,YAMI,WANJ,mBAEI,CAIA,8DAMF,qCAEA,uEAIA,CAlBF,UAkBE,wCAEA,8FAIA,aACA,CAIA,wEAGI,6DCjEN,yEAKE,mCAGA,CACA,2BACA,+BACA,SrCKI,oNqCAN,UrCKQ,SqCLR,qFPLE,mCACA,CAJA,SAIA,wCOQF,oFAIE,eAEA,gDAQF,kBACE,CAOA,eAGF,SAhBI,QACA,CAUF,UACA,CAXE,YAIJ,CACE,sBACA,CASF,UACE,CAPF,SACE,CAZE,iBACA,CAYF,iBACA,CAbE,MAiBF,4BAGA,CAbA,SAGF,CATI,SAmBF,wCACA,6DAGF,sHAKE,WAKA,UACA,CAHF,SAEE,CALA,oBAMA,CACA,uBACA,MACA,wFAQA,uBACA,CAHA,2BAEA,CACA,yBACA,CATA,oBAGF,YAEE,CAFF,UAME,mTC7DA,6BAIA,uRAgBE,CAIJ,qBACE,mEACA,CADA,iGACA,uCAME,4BADF,qBACE,SAFJ,CAEI,qCAFJ,iCAEI,CALF,uBAGF,cACE,CAJA,wCAGF,CAHE,iBAKE,WALF,UAGF,mBAEI,2BH7CA,CGwCF,UHxCE,wCIFF,sCAII,8CAJJ,SACE,mBAGE,cAEE,CAFF,WAEE,QANN,CACE,sBAGE,CAJJ,mBACE,CAGE,6CAEE,uFANN,uCAII,gCAEE,sDAFF,qBAEE,kCAFF,6CAEE,kCAFF,oDCFJ,kCAGA,wBAEE,kBAeA,qDADF,CACE,6CCpBF,CDoBE,kBADF,kBACE,qCADF,CAdE,oBACA,CAIA,kCAGA,CAPA,UCNF,oBAIA,iBAGF,CAJE,WACA,CAFA,UAKF,iCAIE,6BASE,mBACE,qCAEA,ejDqCF,0BiDxCA,6BACE,mBACA,KACA,gCjDqCF,eiDxCA,gBACE,mDADF,4CACE,CjDuCF,6BiDvCE,wCACA,WACA,CAFA,UAEA,sBjDqCF,CiDvCE,4BjDuCF,YiDrCE,UjDqCF,wCiDvCE,6DCtBJ,wBACA,aCCF,4BDQE,qBCRF,CDCE,SAGF,YACE,sBAEA,gBCPF,UDAE,cACA,CCDF,4CCIE,qCAEA,uEAGA,CFFA,iBACA,CAPA,YEQA,wCAEA,0BACA,sBCLE,qBCPJ,CDKI,YAEA,CALA,OADF,eACE,MAEA,WACA,CAHA,YCFJ,0BCAE,SACA,0BACA,UCNF,mBAEE,kBACA,CAFA,YACA,CACA,6BAEA,aACA,8BCgEU,oBAPJ,oBAOI,kBAPJ,CAOI,aAPJ,kBAOI,eAPJ,CAOI,eAPJ,iBAOI,YAPJ,eAOI,+BAPJ,sCAOI,MAPJ,CAOI,MAPJ,+DAOI,CAPJ,WAOI,gBAPJ,qCAOI,cAPJ,mCAOI,0BAPJ,CAOI,WAPJ,uDAOI,MAPJ,mCAOI,8DAPJ,WAOI,CAPJ,OAOI,eAPJ,CAOI,iLAPJ,2CAOI,CAPJ,YAOI,oBAPJ,eAOI,CAPJ,UAOI,0DAPJ,oBAOI,iBAPJ,eAOI,kEAPJ,+BAOI,6GAPJ,CAOI,oCAPJ,cAOI,8BAPJ,cAOI,oWAPJ,oHAOI,sBAPJ,4BAOI,2GAPJ,iBAOI,sFAPJ,sDAOI,sFAPJ,eAOI,aAPJ,yCAOI,aAPJ,cAOI,8EAPJ,aAOI,2HAPJ,yBAOI,0BAPJ,UAOI,CAPJ,gDAOI,sBAPJ,OAOI,kBAPJ,gBAOI,mEAPJ,aAOI,yBAPJ,4CAOI,iBAPJ,0BAOI,OAPJ,cAOI,sBAPJ,sBAOI,aAPJ,uBAOI,sBAPJ,aAOI,0BAPJ,eAOI,uBAPJ,gBAOI,mBAPJ,2BAOI,eAPJ,uBAOI,mCAPJ,EAOI,yBAPJ,eAOI,uCAPJ,MAOI,wDAPJ,uBAOI,CAPJ,eAOI,4GAPJ,aAOI,UAPJ,kBAOI,mBAPJ,iBAOI,kBAPJ,CAOI,YAPJ,SAOI,aAPJ,kGAOI,6BAPJ,kBAOI,CAPJ,oBAOI,CAPJ,sBAOI,8CAPJ,2BAOI,6BAPJ,CAOI,0CAPJ,QAOI,WAPJ,CAOI,yBAPJ,QAOI,KAPJ,CAOI,SAPJ,gBAOI,eAPJ,uBAOI,mBAPJ,KAOI,gDAPJ,qBAOI,CAPJ,cAOI,CAPJ,WAOI,2BAPJ,iCAOI,uDAPJ,+BAOI,wJAPJ,cAOI,gCAPJ,kCAOI,gCAPJ,gCAOI,kCAPJ,oBAOI,kCAPJ,cAOI,mCAPJ,uBAOI,kBAPJ,yBAOI,6CAPJ,kBAOI,yBAPJ,WAOI,yCAPJ,8BAOI,iCAPJ,SAOI,gCAPJ,uBAOI,yCAPJ,eAOI,qCAPJ,sBAOI,6CAPJ,SAOI,sBAPJ,sEAOI,iIAPJ,yBAOI,kBAPJ,yBAOI,mEAPJ,4CAOI,4PAPJ,qBAOI,UAPJ,gBAOI,yCAPJ,mBAOI,qSAPJ,qBAOI,4CAPJ,qCAOI,qFAPJ,sCAOI,oVAPJ,iDAOI,+CAPJ,4CAOI,6FAPJ,6CAOI,yFAPJ,2BAOI,WAPJ,0BAOI,WAPJ,0BAOI,WAPJ,0BAOI,iFAPJ,mBAOI,oDAPJ,QAOI,oBAPJ,SAOI,6BAPJ,wBAOI,SAPJ,qBAOI,aAPJ,yBAOI,OAPJ,oBAOI,+LAPJ,0BAOI,CAPJ,WAOI,wLAPJ,uCAOI,sFAPJ,uBAOI,uCAPJ,YAOI,oHAPJ,QAOI,6VAPJ,uCAOI,yBAPJ,+DAOI,uCAPJ,mBAOI,iIAPJ,uBAOI,iFAPJ,sBAOI,sDAPJ,gCAOI,CAPJ,sBAOI,kHAPJ,4DAOI,6TAPJ,cAOI,wEAPJ,UAOI,iBAPJ,2BAOI,sDAPJ,aAOI,uBAPJ,kBAOI,6BAPJ,MAOI,4BAPJ,qBAOI,wDAPJ,SAOI,qBAPJ,8BAOI,4HAPJ,CAOI,4BAPJ,CAOI,MAPJ,0BAOI,mCAPJ,4BAOI,qCAPJ,0BAOI,sCAPJ,0BAOI,wHAPJ,CAOI,2BAPJ,OAOI,8BAPJ,0BAOI,oEAPJ,8BAOI,+HAPJ,CAOI,yBAPJ,6BAIQ,OAGJ,8HAPJ,gCAOI,yIAHI,kCAGJ,qCAPJ,MAOI,gEAPJ,gCAOI,iJAHI,OAGJ,mIAPJ,OAIQ,2BAGJ,+GAPJ,0BAIQ,MAGJ,wIAHI,4BAGJ,6FAHI,CAGJ,8BAHI,OAGJ,4BAPJ,CAOI,6FAPJ,MAOI,8BAPJ,8BAOI,sEAPJ,0BAIQ,CAGJ,uBAHI,CAGJ,MAPJ,+BAOI,oCAPJ,8BAOI,mCAPJ,6BAIQ,CAGJ,iCAjBJ,gCAiBI,4BAjBJ,OACE,8BADF,0BACE,8BADF,OACE,4BAaM,kCAGJ,qIAPJ,OAIQ,8BAGJ,+IAHI,iCAGJ,qJAHI,CAGJ,0IAPJ,iCAOI,uUAHI,OAGJ,wJAHI,aAGJ,iJAHI,qCAGJ,iHAPJ,0BAIQ,UAGJ,oIAPJ,aAIQ,2BAGJ,qDAPJ,CAOI,6FAHI,sCAGJ,iBAjBJ,kCACE,CADF,gBACE,kCADF,kBACE,+CADF,4BAUA,cAOI,imBAPJ,wDAOI,gEAPJ,eAOI,0EAPJ,mEAOI,iCAPJ,CAOI,8RAPJ,mBAOI,uHAPJ,+BAOI,mDAPJ,iEAOI,aAPJ,mBAOI,yDAPJ,iEAOI,YAPJ,mBAOI,oLAPJ,gBAOI,qFAPJ,mCAOI,aAPJ,oBAOI,yCAPJ,sBAOI,kBAPJ,qBAOI,kBAPJ,sBAOI,mBAPJ,mBAOI,8BAPJ,iEAOI,2FAPJ,iBAOI,gJAPJ,aAOI,6JAPJ,2BAOI,iEAPJ,CAOI,sKAPJ,4EAOI,6BAPJ,2IAOI,4BAPJ,4IAOI,4BAPJ,kJAOI,6EAPJ,0EAOI,4BAPJ,sJAOI,iBAPJ,8IAOI,iBAPJ,iBAOI,uCAPJ,CAOI,sFAPJ,mBAOI,gBAPJ,oBAOI,uaAPJ,+BAOI,2BAPJ,UAOI,wHAPJ,yBAOI,yCAPJ,YAOI,mHAPJ,eAOI,kFAPJ,2BAOI,wCAPJ,8BAOI,uTAPJ,eAOI,qBAPJ,gBAOI,oBAPJ,cAOI,wBAPJ,oBAOI,oGAPJ,CAOI,mCAPJ,iBAOI,2BAPJ,kBAOI,0HAPJ,sBAOI,eAPJ,uBAOI,cAPJ,4BAOI,iBAPJ,qDAOI,oCAPJ,yBAOI,uCAPJ,iBAOI,qBAPJ,iBAOI,gJAPJ,iBAOI,iDAPJ,gCAOI,qCAPJ,oBAOI,iPAPJ,4BAOI,sYAPJ,0BAOI,oFAPJ,yBAOI,yFAPJ,0BAOI,yDAPJ,qCAOI,4QAPJ,uBAOI,oDAPJ,6BAOI,oDAPJ,iBAOI,kBAPJ,aAOI,8BAPJ,iBAOI,8BzDPR,8ByDAI,aAOI,iBAPJ,aAOI,iBAPJ,gBAOI,0BAPJ,kBAOI,gCAPJ,CAOI,8BAPJ,8BAOI,SAPJ,uBAOI,SAPJ,qBAOI,2CAPJ,uBAOI,kIAPJ,CAOI,4BAPJ,oCAOI,kEAPJ,CAOI,6BAPJ,UAOI,0BAPJ,yCAOI,gEAPJ,yBAOI,uBAPJ,UAOI,+BAPJ,2BAOI,uCAPJ,CAOI,0BAPJ,sCAOI,2KAPJ,4BAOI,CAPJ,yDAOI,qCAPJ,oCAOI,mCAPJ,UAOI,qCAPJ,yBAOI,sCAPJ,kCAOI,uCAPJ,sCAOI,qCAPJ,UAOI,6BAPJ,qCAOI,2EAPJ,wCAOI,UAPJ,6BAOI,sCAPJ,UAOI,wCAPJ,4BAOI,yCAPJ,UAOI,iCAPJ,4BAOI,qCAPJ,oCAOI,sCAPJ,UAOI,2BAPJ,sCAOI,SAPJ,4BAOI,wBAPJ,gCAOI,+BAPJ,SAOI,iCAPJ,sBAOI,UAPJ,wBAOI,8KAPJ,+HAOI,wQAPJ,SAOI,8BAPJ,0BAOI,yCAPJ,uCAOI,6BAPJ,CAOI,0BAPJ,UAOI,6DAPJ,UAOI,6KAPJ,uCAOI,qKAPJ,0BAOI,yCAPJ,UAOI,wHAPJ,uCAOI,kCAPJ,uCAOI,4BAPJ,UAOI,2BAPJ,UAOI,6BAPJ,UAOI,2BAPJ,gBAOI,4KAPJ,eAOI,qBAPJ,gBAOI,oBAPJ,cAOI,wBAPJ,oBAOI,oGAPJ,CAOI,mCAPJ,iBAOI,2BAPJ,kBAOI,0HAPJ,sBAOI,eAPJ,uBAOI,cAPJ,4BAOI,iBAPJ,qDAOI,oCAPJ,yBAOI,uCAPJ,iBAOI,qBAPJ,iBAOI,gJAPJ,iBAOI,iDAPJ,gCAOI,qCAPJ,oBAOI,iPAPJ,4BAOI,sYAPJ,0BAOI,oFAPJ,yBAOI,yFAPJ,0BAOI,yDAPJ,qCAOI,4QAPJ,uBAOI,oDAPJ,6BAOI,oDAPJ,iBAOI,kBAPJ,aAOI,8BAPJ,iBAOI,8BzDPR,8ByDAI,aAOI,iBAPJ,aAOI,iBAPJ,gBAOI,0BAPJ,kBAOI,gCAPJ,CAOI,8BAPJ,8BAOI,SAPJ,uBAOI,SAPJ,qBAOI,2CAPJ,uBAOI,kIAPJ,CAOI,4BAPJ,oCAOI,kEAPJ,CAOI,6BAPJ,UAOI,0BAPJ,yCAOI,gEAPJ,yBAOI,uBAPJ,UAOI,+BAPJ,2BAOI,uCAPJ,CAOI,0BAPJ,sCAOI,2KAPJ,4BAOI,CAPJ,yDAOI,qCAPJ,oCAOI,mCAPJ,UAOI,qCAPJ,yBAOI,sCAPJ,kCAOI,uCAPJ,sCAOI,qCAPJ,UAOI,6BAPJ,qCAOI,2EAPJ,wCAOI,UAPJ,6BAOI,sCAPJ,UAOI,wCAPJ,4BAOI,yCAPJ,UAOI,iCAPJ,4BAOI,qCAPJ,oCAOI,sCAPJ,UAOI,2BAPJ,sCAOI,SAPJ,4BAOI,wBAPJ,gCAOI,+BAPJ,SAOI,iCAPJ,sBAOI,UAPJ,wBAOI,8KAPJ,+HAOI,wQAPJ,SAOI,8BAPJ,0BAOI,yCAPJ,uCAOI,6BAPJ,CAOI,0BAPJ,UAOI,6DAPJ,UAOI,6KAPJ,uCAOI,qKAPJ,0BAOI,yCAPJ,UAOI,wHAPJ,uCAOI,kCAPJ,uCAOI,4BAPJ,UAOI,2BAPJ,UAOI,6BAPJ,UAOI,2BAPJ,gBAOI,4KAPJ,eAOI,qBAPJ,gBAOI,oBAPJ,cAOI,wBAPJ,oBAOI,oGAPJ,CAOI,mCAPJ,iBAOI,2BAPJ,kBAOI,0HAPJ,sBAOI,eAPJ,uBAOI,cAPJ,4BAOI,iBAPJ,qDAOI,oCAPJ,yBAOI,uCAPJ,iBAOI,qBAPJ,iBAOI,gJAPJ,iBAOI,iDAPJ,gCAOI,qCAPJ,oBAOI,iPAPJ,4BAOI,sYAPJ,0BAOI,oFAPJ,yBAOI,yFAPJ,0BAOI,yDAPJ,qCAOI,4QAPJ,uBAOI,oDAPJ,6BAOI,oDAPJ,iBAOI,kBAPJ,aAOI,8BAPJ,iBAOI,8BzDPR,8ByDAI,aAOI,iBAPJ,aAOI,iBAPJ,gBAOI,0BAPJ,kBAOI,gCAPJ,SAOI,uBAPJ,6BAOI,SAPJ,uBAOI,SAPJ,qBAOI,2CAPJ,uBAOI,uGAPJ,4BAOI,4BAPJ,oCAOI,CAPJ,2BAOI,sCAPJ,CAOI,6BAPJ,UAOI,0BAPJ,yCAOI,0BAPJ,CAOI,qCAPJ,yBAOI,wBAPJ,SAOI,+BAPJ,2BAOI,uCAPJ,CAOI,0BAPJ,sCAOI,2KAPJ,4BAOI,CAPJ,yBAOI,CAPJ,+BAOI,qCAPJ,oCAOI,mCAPJ,qCAOI,UAPJ,yBAOI,uCAPJ,iCAOI,uCAPJ,sCAOI,qCAPJ,UAOI,8BAPJ,oCAOI,2EAPJ,wCAOI,UAPJ,6BAOI,sCAPJ,UAOI,wCAPJ,4BAOI,yCAPJ,UAOI,iCAPJ,4BAOI,qCAPJ,oCAOI,sCAPJ,UAOI,uCAPJ,0BAOI,SAPJ,4BAOI,yBAPJ,+BAOI,+BAPJ,iCAOI,SAPJ,sBAOI,UAPJ,wBAOI,8KAPJ,2BAOI,CAPJ,4GAOI,sSAPJ,0BAOI,0CAPJ,sCAOI,6BAPJ,CAOI,0BAPJ,UAOI,6DAPJ,UAOI,sLAPJ,8BAOI,uCAPJ,CAOI,6HAPJ,0BAOI,yCAPJ,UAOI,wHAPJ,uCAOI,kCAPJ,uCAOI,4BAPJ,UAOI,2BAPJ,UAOI,6BAPJ,UAOI,2BAPJ,gBAOI,6KAPJ,eAOI,qBAPJ,gBAOI,oBAPJ,cAOI,wBAPJ,oBAOI,oGAPJ,CAOI,mCAPJ,iBAOI,2BAPJ,kBAOI,0HAPJ,sBAOI,eAPJ,uBAOI,cAPJ,4BAOI,iBAPJ,qDAOI,oCAPJ,yBAOI,uCAPJ,iBAOI,qBAPJ,iBAOI,gJAPJ,iBAOI,iDAPJ,gCAOI,qCAPJ,oBAOI,iPAPJ,4BAOI,sYAPJ,0BAOI,oFAPJ,yBAOI,yFAPJ,0BAOI,yDAPJ,qCAOI,4QAPJ,uBAOI,oDAPJ,6BAOI,oDAPJ,iBAOI,kBAPJ,aAOI,8BAPJ,iBAOI,8BzDPR,8ByDAI,aAOI,iBAPJ,aAOI,iCAPJ,iBAOI,SAPJ,kBAOI,gCAPJ,SAOI,+BAPJ,qBAOI,+FAPJ,iCAOI,+DAPJ,CAOI,6BAPJ,UAOI,4BAPJ,4BAOI,qCAPJ,2BAOI,UAPJ,4BAOI,mEAPJ,2BAOI,wCAPJ,2BAOI,CAPJ,SAOI,yBAPJ,uBAOI,UAPJ,+BAOI,2BAPJ,UAOI,8BAPJ,0BAOI,sCAPJ,CAOI,yBAPJ,UAOI,+BAPJ,2BAOI,uCAPJ,yBAOI,aAPJ,4BAOI,CAPJ,yBAOI,UAPJ,2DAOI,oCAPJ,mCAOI,qCAPJ,UAOI,sCAPJ,yBAOI,4CAPJ,6BAOI,sCAPJ,qCAOI,uCAPJ,UAOI,wCAPJ,2BAOI,mCAPJ,UAOI,8BAPJ,uCAOI,UAPJ,4BAOI,wCAPJ,sCAOI,aAPJ,4BAOI,iCAPJ,sCAOI,UAPJ,4BAOI,6CAPJ,4BAOI,oCAPJ,uCAOI,4BAPJ,SAOI,wBAPJ,gCAOI,+BAPJ,SAOI,iCAPJ,sBAOI,UAPJ,wBAOI,oCAPJ,6BAOI,yCAPJ,4BAOI,CAPJ,6BAOI,UAPJ,wIAOI,4FAPJ,CAOI,6QAPJ,4BAOI,wCAPJ,0BAOI,UAPJ,uBAOI,sCAPJ,qCAOI,oCAPJ,UAOI,gEAPJ,CAOI,iHAPJ,sCAOI,wCAPJ,UAOI,mHAPJ,8BAOI,uCAPJ,UAOI,2QAPJ,2BAOI,gBAPJ,yBAOI,cAPJ,0BAOI,4CAPJ,4BAOI,qDAPJ,qBAOI,iBAPJ,oBAOI,eAPJ,wBAOI,qBAPJ,8BAOI,CAPJ,aAOI,uBAPJ,aAOI,oCAPJ,uBAOI,kBAPJ,2BAOI,+CAPJ,aAOI,uBAPJ,mBAOI,6BAPJ,aAOI,sBAPJ,gBAOI,uBAPJ,eAOI,oGAPJ,8DAOI,uCAPJ,kBAOI,qBAPJ,kBAOI,yCAPJ,uBAOI,2CAPJ,gBAOI,4HAPJ,CAOI,0DAPJ,YAOI,6DAPJ,oBAOI,wHAPJ,kCAOI,6BAPJ,gCAOI,8BAPJ,uCAOI,6BAPJ,sCAOI,6BAPJ,sCAOI,4GAPJ,yBAOI,qFAPJ,0BAOI,yFAPJ,wBAOI,yFAPJ,4BAOI,gIAPJ,+BAOI,+CAPJ,uBAOI,yGAPJ,2BAOI,8HAPJ,kBAOI,8DAPJ,cAOI,+BAPJ,iBAOI,cAPJ,iBAOI,+BCnDZ,kCDmDY,4HChCZ,UDyBQ,uBAOI,+BAPJ,aAOI,uDAPJ,yBAOI,uCAPJ,CAOI,6BAPJ,sCAOI,CAPJ,4BAOI,qCAPJ,CAOI,2BAPJ,WAOI,6BAPJ,6BAOI,+uIEtEZ,KACE,iBACF,CAEA,UACE,aAAc,CACd,mBACF,CAEA,8CACE,UACE,mDAA4C,CAA5C,2CACF,CACF,CAEA,SAIE,kCAA2C,CAC3C,2BAAgC,CAAhC,8BAAgC,CAJhC,sBAKF,CAEA,OACE,uBAAyB,CACzB,eAAiB,CACjB,kBACF,CAEA,YAKE,kBAAmB,CAJnB,wBAAyB,CAOzB,UAAY,CALZ,YAAa,CACb,qBAAsB,CAGtB,4BAA6B,CAD7B,sBAAuB,CAJvB,gBAOF,CAEA,UACE,aACF,CAEA,iCACE,GACE,8BAAuB,CAAvB,sBACF,CACA,GACE,+BAAyB,CAAzB,uBACF,CACF,CAPA,yBACE,GACE,8BAAuB,CAAvB,sBACF,CACA,GACE,+BAAyB,CAAzB,uBACF,CACF,CAEA,iBACE,eACF,CAEA,gBACE,qBAAsB,CAItB,cAAe,CADf,cAAe,CADf,kBAAmB,CADnB,YAIF,CAEA,aACE,cACF,CAEA,uBACE,kBAAmB,CACnB,UACF,CAEA,qBACE,kBAKF,CAEA,uCANE,kBAAmB,CAEnB,WAAY,CACZ,gBAAiB,CAFjB,UAWF,CANA,kBACE,kBAKF,CAEA,mBACE,kBAKF,CAEA,uCANE,kBAAmB,CAEnB,WAAY,CACZ,gBAAiB,CAFjB,UAWF,CANA,oBACE,kBAKF,CAEA,mBACE,eAAgB,CAChB,kBAAmB,CAEnB,WAAY,CACZ,gBAAiB,CAFjB,UAGF,CAUA,uCAHE,wBAQF,CALA,wBAIE,aAAc,CADd,cAAe,CADf,gBAGF,CAEA,YACE,UACF,CAEA,gBACE,iBACF,CAEA,gBACE,iBACF,CAEA,OAGE,WAAY,CAFZ,eAAgB,CAChB,eAAgB,CAEhB,WACF,CAEA,EACE,cAAe,CACf,mCACF,CAEA,iBAEE,WAGF,CAEA,0BANE,eAAgB,CAEhB,iBAAkB,CAClB,kBASF,CANA,SAEE,WAAY,CADZ,UAKF,CAgBA,0DAEE,WAAY,CADZ,eAAgB,CAEhB,iBAAkB,CAClB,kBACF,CAEA,qBACE,eACF,CAEA,WACE,cACF,CAEA,iBAEE,UAAW,CADX,cAEF,CAEA,iBAEE,UAAW,CADX,cAEF,CAEA,+DACE,iBACF,CAEA,2BACE,kBAAmB,CACnB,aACF,CAEA,kCACE,UACF,CAEA,gCACE,kBAAmB,CACnB,UACF,CAEA,iCACE,kBAAmB,CACnB,UACF,CAEA,iCACE,eAAgB,CAChB,UACF,CAEA,mCACE,kBAAmB,CACnB,UACF,CAEA,0CACE,UACF,CAEA,kCACE,kBAAmB,CACnB,UACF,CAEA,iCACE,kBAAmB,CACnB,UACF,CAEA,kCACE,kBAAmB,CACnB,UACF,CAEA,6BACE,eAAgB,CAChB,UACF,CAEA,8BACE,eAAkB,CAClB,UACF,CAEA,6BACE,eAAgB,CAChB,UACF,CAEA,eAKE,aAAc,CAJd,cAAe,CAGf,cAAe,CAFf,eAAgB,CAChB,iBAGF,CAEA,eAEE,YAAa,CADb,WAEF,CAEA,0CAKE,aAAc,CAJd,cAAe,CAGf,cAAe,CAFf,eAAgB,CAChB,iBAAkB,CAGlB,yBACF,CAEA,oBACE,eAAgB,CAChB,WACF,CAEA,UACE,kBACF,CAEA,qBACE,eAAgB,CAChB,YAAa,CACb,UACF,CAEA,8BAME,kBAAmB,CALnB,sBAAuB,CAOvB,WAAY,CAHZ,YAAa,CAEb,cAAe,CAHf,kBAAmB,CAFnB,YAAa,CACb,UAMF,CAEA,kBAEE,WAAW,CACX,iBAAkB,CAFlB,UAGF,CAEA,2BAEE,cAAe,CADf,iBAEF,CAEA,aAEE,kBAAmB,CADnB,YAAa,CAEb,kBAAmB,CACnB,YACF,CAEA,qBACE,gBACF,CAEA,yCACE,WAAY,CACZ,UACF,CAEA,aACE,8BACF,CAEA,OAEE,WAAY,CADZ,iBAEF,CAEA,iBACE,eAAgB,CAChB,gBACF,CAMA,kCACE,eACF,CAEA,wBACE,eAAgB,CAChB,YACF,CAEA,SACE,aACF,CAEA,aAIE,uEAAsD,CAGtD,0BAA8B,CAC9B,2BAA4B,CAH5B,WAAY,CAJZ,cAAe,CACf,OAAQ,CACR,KAAM,CAGN,UAAW,CAGX,UACF,CAEA,uBACE,YAAa,CACb,cACF,CAEA,eACE,UACF,CAEA,iBACE,kBAAmB,CACnB,gBAAiB,CACjB,eACF,CAEA,mBACE,gBACF,CAEA,oBACE,kBACF,CAEA,MAKE,kBAAmB,CADnB,YAAa,CADb,SAAU,CAFV,iBAAkB,CAClB,QAIF,CAEA,aAIE,YAAa,CAHb,cAAe,CAEf,eAAgB,CADhB,eAGF,CAEA,aACE,0BAA4B,CAC5B,2BACF,CAEA,eACE,UAAY,CAEZ,aAAc,CADd,8BAEF,CAEA,UACE,WACF,CAEA,sBAEE,YAAa,CACb,kBAAmB,CAInB,cAAe,CAHf,sBAAuB,CACvB,oBAAqB,CAJrB,kBAAmB,CAKnB,SAEF,CAEA,2BAKE,sBAAuB,CAJvB,iBAAkB,CAKlB,UAAW,CAFX,cAAe,CAFf,mBAKF,CAEA,yBACE,WACF,CAEA,2BACE,WAAY,CACZ,8BACF,CAEA,qGAIE,sBAAuB,CADvB,wBAEF,CAEA,kCACE,kCAAoC,CACpC,wBAAyB,CACzB,UAAY,CACZ,cACF,CAEA,oCAEE,gCAAkC,CADlC,UAEF,CAEA,qEAGE,sBAAuB,CADvB,cAEF,CAEA,2BACE,WAAY,CACZ,QAAS,CAET,cAAe,CADf,WAEF,CAEA,wBACE,eACF,CAEA,WACE,qBACF,CAEA,iBAEE,WAAY,CAGZ,gBAAiB,CAJjB,wBAA0B,CAG1B,gBAAiB,CADjB,aAGF,CAEA,iBAQE,kBAAmB,CAJnB,eAAiB,CAEjB,YAAa,CAJb,WAAY,CAKZ,sBAAuB,CAGvB,MAAM,CAPN,UAAY,CAEZ,cAAc,CAId,KAAK,CARL,UAUF,CAEA,0CACE,WACE,gBACF,CACF,CAEA,yCACE,SACE,UACF,CACF,CAEA,0CACE,WACE,gBACF,CACF,CAEA,aACE,MAAO,cAAe,CACxB,CAEA,aACE,SACE,sBACF,CACA,WACE,aAAc,CACd,cACF,CACF,CCpjBA,aACE,aAAc,CACd,iBACF,CACA,qEAME,qBACF,CACA,mBAME,qBAAuB,CACvB,iBAAkB,CAJlB,QAAS,CACT,MAAO,CAHP,iBAAkB,CAIlB,OAAQ,CAHR,KAMF,CACA,mBAGE,QAAS,CACT,QAAS,CAHT,iBAAkB,CAIlB,SAAU,CAHV,KAIF,CACA,yBAEE,qBAAuB,CADvB,iBAAkB,CAElB,kCAA2B,CAA3B,0BACF,CACA,mBAGE,QAAS,CACT,QAAS,CAHT,iBAAkB,CAIlB,SAAU,CAHV,KAIF,CACA,yBAEE,qBAAuB,CADvB,iBAAkB,CAElB,kCAA2B,CAA3B,0BACF,CACA,2BAEE,UAAW,CADX,iBAAkB,CAGlB,iBAAkB,CADlB,UAEF,CACA,gCACE,oBACF,CCrDA,mBACE,mBAAoB,CACpB,iBACF,CACA,6FAME,qBACF,CACA,6BACE,wBAAyB,CACzB,aACF,CACA,4BAIE,sBAAuB,CAHvB,YAAa,CACb,WAAY,CACZ,aAEF,CACA,+BAIE,sBAAuB,CAFvB,WAAY,CADZ,8BAAwD,CAExD,aAEF,CACA,wCACE,aAAc,CACd,eACF,CACA,sCASE,yBAA0B,CAH1B,eAAgB,CADhB,QAAS,CAGT,sBAAuB,CADvB,YAAa,CALb,WAAY,CADZ,eAAiB,CAGjB,aAAc,CADd,iBAOF,CACA,kIAEE,uBAAwB,CACxB,QACF,CACA,8CACE,2BACF,CACA,sDACE,kBAAoB,CACpB,8BACF,CACA,qCAEE,wBAAyB,CADzB,YAEF,CACA,2BAEE,sBAAuB,CADvB,QAAS,CAET,eACF,CACA,mCACE,cACF,CACA,oJAEE,cACF,CACA,qEACE,cACF,CACA,+BACE,eACF,CACA,0BAKE,qBAAuB,CACvB,yBAA0B,CAJ1B,YAAa,CAOb,MAAO,CANP,eAAgB,CAChB,YAAa,CAGb,iBAAkB,CAClB,QAAS,CAPT,WAAY,CASZ,SACF,CACA,kCACE,YACF,CC1FA,2LAKE,iBAAyB,CAAzB,kBAAyB,CAAzB,wBAAyB,CACzB,UAAW,CACX,aAAc,CACd,UAAW,CACX,iBAAkB,CAClB,OAAQ,CACR,SACF,CACA,uJACE,gBAAiB,CACjB,iBAAkB,CAClB,OACF,CACA,wUAQE,4BAAiB,CAPjB,sBAAuB,CAKvB,UAAW,CAFX,QAAS,CAKT,SAAU,CAPV,iBAAkB,CAGlB,SAAU,CAEV,UAGF,CACA,qKACE,2BACF,CAEA,6EAEE,eAAgB,CADhB,KAEF,CACA,uKAEE,2BAA4B,CAD5B,eAEF,CACA,mFACE,KACF,CACA,oFAEE,2BAA4B,CAD5B,QAEF,CAEA,0EACE,QAAS,CACT,kBACF,CACA,iKACE,kBAAmB,CACnB,qBACF,CACA,gFACE,QACF,CACA,iFAEE,wBAAyB,CADzB,WAEF,CAEA,0BAGE,QAAS,CAFT,oBAAqB,CACrB,SAAU,CAEV,UACF,CAEA,kBAGE,qBAAsB,CAEtB,wBAAyB,CACzB,mBAAqB,CAFrB,UAAW,CAGX,oBAAqB,CANrB,qDAA2D,CAC3D,eAAiB,CAMjB,iBACF,CAEA,yDACE,SACF,CACA,+DACE,aACF,CACA,8GAEE,+BAAiC,CACjC,gCACF,CAEA,4BAEE,SAAU,CADV,iBAEF,CAEA,yBACE,SACF,CACA,iDACE,gBACF,CACA,6JACE,SAAU,CACV,UACF,CACA,8CACE,mBACF,CACA,gDACE,gBACF,CACA,4EACE,SAAU,CACV,UACF,CACA,+CACE,iBACF,CACA,2EACE,SAAU,CACV,UACF,CAEA,0BAEE,wBAAyB,CACzB,+BAAgC,CAChC,4BAA8B,CAC9B,aAAc,CACd,iBAAkB,CALlB,iBAMF,CACA,gCACE,kBAAmB,CACnB,gBAAiB,CACjB,iBACF,CACA,2EACE,wBACF,CACA,0EACE,6BACF,CAEA,gUAME,oBAAqB,CACrB,YACF,CAEA,8FAIE,UAAW,CAEX,iBAAmB,CADnB,eAAiB,CAFjB,YAIF,CAEA,+BAGE,eAAgB,CAFhB,sBAAuB,CACvB,kBAEF,CAEA,8BACE,kBAAmB,CACnB,eAAgB,CAQhB,WAAY,CAJZ,cAAe,CAHf,YAAa,CASb,WAAY,CARZ,sBAAuB,CAWvB,eAAgB,CANhB,SAAU,CAFV,iBAAkB,CAFlB,iBAAkB,CASlB,kBAAmB,CANnB,OAAQ,CAKR,UAAW,CAFX,SAKF,CACA,wCACE,QACF,CACA,oCACE,SACF,CACA,2GACE,UACF,CACA,qCAGE,aAAc,CACd,gBAAiB,CACjB,iBAAkB,CAJlB,iBAAkB,CAClB,KAIF,CACA,8CACE,OACF,CACA,8CACE,QACF,CACA,4CACE,oBACF,CAEA,mCAGE,cAAe,CAFf,iBAAkB,CAClB,QAAS,CAET,OACF,CACA,yCACE,SACF,CACA,gDAEE,SAAU,CADV,+BAAwB,CAAxB,uBAEF,CACA,6CACE,UACF,CACA,oDAEE,UAAW,CADX,gCAAyB,CAAzB,wBAEF,CAEA,mCACE,UACF,CAEA,wBACE,YAAc,CACd,iBACF,CACA,gCACE,YAAa,CACb,cAAe,CACf,eACF,CACA,qDACE,oBAAqB,CAErB,UAAW,CADX,UAEF,CAEA,yBACE,YAAc,CACd,iBACF,CACA,gHAEE,oBAAqB,CAErB,UAAW,CADX,UAEF,CAEA,wCACE,UAAW,CAEX,UAAW,CACX,sBAAuB,CACvB,eAAgB,CAHhB,UAIF,CAIA,wJACE,oBACF,CACA,8GACE,oBAAqB,CACrB,gBACF,CACA,oHACE,UACF,CACA,oTAEE,uBAAwB,CACxB,QACF,CACA,+HACE,yBACF,CACA,kHAEE,oBAAqB,CADrB,eAEF,CAEA,kCAEE,6BAA8B,CAD9B,WAAY,CAEZ,UACF,CACA,qDAEE,wBAAyB,CACzB,mBAAqB,CAFrB,cAAe,CAGf,iBAAkB,CAClB,WAAY,CACZ,KACF,CACA,0DAEE,eAAiB,CACjB,gCAAkC,CAFlC,iBAGF,CACA,sFAKE,gCAAkC,CAFlC,aAAc,CADd,iBAAkB,CAElB,iBAAkB,CAHlB,UAKF,CACA,qHAQE,sBAAuB,CALvB,2BAAkC,CAFlC,eAAgB,CAChB,QAAS,CAET,iBAAkB,CAElB,cAAe,CADf,eAAgB,CAEhB,UAEF,CACA,yJACE,WAAY,CACZ,gBAAiB,CACjB,kBACF,CACA,+JAEE,wBAAyB,CADzB,cAEF,CACA,mKACE,wBAAyB,CACzB,UAAY,CACZ,eACF,CACA,yKACE,wBACF,CACA,mKACE,UACF,CACA,yKAEE,4BAA6B,CAD7B,cAEF,CAEA,+BACE,UAAW,CACX,oBAAqB,CAErB,kBAAmB,CAEnB,cAAgB,CADhB,iBAAkB,CAFlB,YAIF,CACA,wEACE,cACF,CACA,8EAEE,wBAAyB,CADzB,mBAEF,CAEA,qDAEE,kBACF,CAEA,6BACE,kBACF,CAEA,gFAGE,UAAW,CACX,oBAAqB,CAErB,kBAAmB,CAEnB,cAAgB,CADhB,iBAAkB,CAFlB,YAIF,CAEA,4OAKE,wBAAyB,CADzB,mBAAqB,CAErB,UACF,CACA,gRAIE,wBACF,CACA,wEAEE,UAAW,CACX,mBACF,CACA,oFAGE,4BAA6B,CAD7B,cAEF,CAEA,kHAIE,cACF,CACA,0IAKE,wBAAyB,CADzB,mBAEF,CACA,8IAIE,eACF,CACA,sKAKE,wBAAyB,CADzB,mBAAqB,CAErB,UACF,CACA,8LAIE,wBACF,CACA,0MAIE,UACF,CACA,0MAIE,WACF,CACA,sfAWE,wBAAyB,CADzB,mBAAqB,CAErB,UACF,CACA,8jBAUE,wBACF,CACA,8LAKE,wBAAyB,CADzB,mBAAqB,CAErB,UACF,CACA,sNAIE,wBACF,CACA,8zBAgBE,oCACF,CACA,8lCAgBE,wBAAyB,CACzB,UACF,CACA,0JAKE,UAAW,CADX,cAEF,CACA,kLAIE,4BACF,CAEA,gkBAKE,wBACF,CACA,0EAEE,wBACF,CAEA,mCAEE,oBAAqB,CADrB,iBAAkB,CAElB,UACF,CAEA,6GAGE,4BAA6B,CAC7B,mBAAqB,CACrB,iBACF,CACA,+HAGE,cACF,CACA,qhBAME,wBACF,CACA,iJAIE,WAAY,CACZ,KAAM,CAFN,gCAAyB,CAAzB,wBAGF,CAEA,0GAGE,wBAAyB,CAQzB,wBAAyB,CADzB,mBAAqB,CAJrB,QAAS,CAFT,iBAAkB,CAKlB,iBAAkB,CAFlB,QAAS,CAFT,SAAU,CAGV,SAIF,CACA,4HAGE,cACF,CACA,8IAGE,YAAa,CACb,iBACF,CAEA,oGAKE,aAAc,CAFd,gBAAiB,CAGjB,gBAAiB,CACjB,iBAAkB,CAHlB,UAIF,CACA,8IAGE,4BAA8B,CAC9B,6BACF,CACA,2IAOE,+BAAiC,CACjC,gCAAkC,CALlC,wBAAyB,CAEzB,oBAAqB,CACrB,gBAGF,CACA,sHAGE,qBACF,CACA,gQAGE,2BACF,CACA,gQAGE,wBACF,CACA,kIAIE,SAAU,CADV,iBAEF,CAEA,8BAEE,4BAA6B,CAC7B,QAAS,CAFT,cAAe,CASf,kBAAmB,CADnB,WAAY,CALZ,SAAU,CACV,iBAAkB,CAClB,iBAAkB,CAElB,OAAQ,CADR,KAAM,CAIN,qBACF,CACA,oCAEE,wBAAyB,CAEzB,iBAAkB,CADlB,UAAW,CAUX,WAAY,CAZZ,cAAe,CAUf,kBAAmB,CAHnB,cAAe,CAHf,WAAY,CAIZ,aAAc,CAFd,WAAY,CAGZ,iBAAkB,CAElB,qBAAsB,CANtB,UAQF,CAEA,gCACE,kBAAmB,CACnB,4BAA6B,CAK7B,UAAW,CAJX,cAAe,CAEf,eAAiB,CACjB,aAAc,CAFd,iBAIF,CAEA,0BAQE,kBAAmB,CAJnB,+BAAoC,CAKpC,YAAa,CANb,YAAa,CAIb,sBAAuB,CAFvB,MAAO,CAJP,cAAe,CAKf,KAAM,CAJN,WAAY,CAQZ,kBACF,CACA,8JAIE,gBAAiB,CADjB,UAEF,CACA,4CACE,8JAIE,gBAAiB,CADjB,UAEF,CACF,CACA,oHAEE,iBACF,CCxuBA,gBAGE,eAAiB,CACjB,wBAAyB,CACzB,sCAAyC,CACzC,mBAAoB,CAJpB,cAAe,CADf,WAMF,CACA,4BACE,WACF,CACA,2DACE,YAAa,CACb,YACF,CACA,6DAEE,WAAa,CADb,SAEF,CACA,iFAME,qBACF,CACA,uBAEE,QAAS,CADT,QAAS,CAET,YACF,CACA,qCACE,cACF,CACA,4BACE,YAAa,CACb,WAAY,CACZ,iBACF,CACA,mCAEE,eAAgB,CADhB,cAEF,CACA,4CACE,wBACF,CACA,kGAEE,wBACF,CACA,sCAIE,eAAiB,CADjB,eAAiB,CAFjB,iBAAkB,CAClB,wBAGF,CACA,+CACE,YACF,CACA,+DAEE,kBAAmB,CADnB,YAAa,CAGb,eAAiB,CACjB,eAAiB,CAFjB,sBAGF,CACA,gDACE,aACF,CACA,yDACE,aACF,CACA,wJAGE,gBACF,CACA,sBAGE,eAAgB,CAEhB,gBAAiB,CAJjB,cAAe,CACf,qBAAsB,CAEtB,iBAEF,CACA,+BACE,wBACF,CACA,wEAEE,wBACF,CACA,2BACE,kBACF,CACA,kFAEE,kBACF,CACA,iCACE,kBACF,CACA,8FAEE,kBACF,CACA,8BACE,kBAAmB,CACnB,UACF,CACA,wFAEE,kBACF,CACA,0DACE,wBACF,CCpHA,uBACE,mBAAoB,CACpB,iBACF,CACA,6GAME,qBACF,CACA,iCACE,wBAAyB,CACzB,aACF,CACA,gCAIE,sBAAuB,CAHvB,YAAa,CACb,WAAY,CACZ,aAEF,CACA,mCAEE,WAAY,CADZ,8BAA8D,CAE9D,aACF,CACA,4CACE,aAAc,CACd,eACF,CACA,0CASE,yBAA0B,CAH1B,eAAgB,CADhB,QAAS,CAGT,sBAAuB,CADvB,YAAa,CALb,uBAAwB,CADxB,eAAiB,CAGjB,WAAY,CADZ,iBAOF,CACA,0IAEE,uBAAwB,CACxB,QACF,CACA,kDACE,2BACF,CACA,0DACE,kBAAoB,CACpB,8BACF,CACA,yCAEE,wBAAyB,CADzB,YAEF,CACA,+BAEE,sBAAuB,CADvB,QAAS,CAET,eACF,CACA,uCACE,cACF,CACA,oKAEE,cACF,CACA,6EACE,cACF,CACA,mCACE,eACF,CACA,+DAIE,MAAO,CAFP,iBAAkB,CAClB,QAAS,CAET,SACF,CACA,+EAEE,YACF,CACA,iCAEE,eAAgB,CADhB,WAEF,CACA,iDACE,iBACF,CACA,8BAKE,qBAAuB,CACvB,yBAA0B,CAJ1B,YAAa,CACb,eAAgB,CAChB,YAAa,CAHb,WAMF","sources":["../node_modules/bootstrap/scss/_type.scss","../node_modules/bootstrap/scss/_images.scss","index.css","../node_modules/bootstrap/scss/bootstrap.scss","../node_modules/bootstrap/scss/_root.scss","../node_modules/bootstrap/dist/css/dist/css/bootstrap.css","../node_modules/bootstrap/scss/_reboot.scss","../node_modules/bootstrap/scss/vendor/_rfs.scss","../node_modules/bootstrap/scss/mixins/_border-radius.scss","../node_modules/bootstrap/scss/mixins/_lists.scss","../node_modules/bootstrap/scss/mixins/_image.scss","../node_modules/bootstrap/scss/_containers.scss","../node_modules/bootstrap/scss/mixins/_container.scss","../node_modules/bootstrap/scss/mixins/_breakpoints.scss","../node_modules/bootstrap/scss/_grid.scss","../node_modules/bootstrap/scss/mixins/_grid.scss","../node_modules/bootstrap/scss/_tables.scss","../node_modules/bootstrap/scss/mixins/_table-variants.scss","../node_modules/bootstrap/scss/forms/_labels.scss","../node_modules/bootstrap/scss/forms/_form-text.scss","../node_modules/bootstrap/scss/forms/_form-control.scss","../node_modules/bootstrap/scss/mixins/_transition.scss","../node_modules/bootstrap/scss/mixins/_gradients.scss","../node_modules/bootstrap/scss/forms/_form-select.scss","../node_modules/bootstrap/scss/forms/_form-check.scss","../node_modules/bootstrap/scss/forms/_form-range.scss","../node_modules/bootstrap/scss/forms/_floating-labels.scss","../node_modules/bootstrap/scss/forms/_input-group.scss","../node_modules/bootstrap/scss/mixins/_forms.scss","../node_modules/bootstrap/scss/_buttons.scss","../node_modules/bootstrap/scss/mixins/_buttons.scss","../node_modules/bootstrap/scss/_transitions.scss","../node_modules/bootstrap/scss/mixins/_caret.scss","../node_modules/bootstrap/scss/_dropdown.scss","../node_modules/bootstrap/scss/_button-group.scss","../node_modules/bootstrap/scss/_nav.scss","../node_modules/bootstrap/scss/_navbar.scss","../node_modules/bootstrap/scss/_card.scss","../node_modules/bootstrap/scss/_accordion.scss","../node_modules/bootstrap/scss/_breadcrumb.scss","../node_modules/bootstrap/scss/_pagination.scss","../node_modules/bootstrap/scss/mixins/_pagination.scss","../node_modules/bootstrap/scss/_badge.scss","../node_modules/bootstrap/scss/_alert.scss","../node_modules/bootstrap/scss/mixins/_alert.scss","../node_modules/bootstrap/scss/_progress.scss","../node_modules/bootstrap/scss/_list-group.scss","../node_modules/bootstrap/scss/mixins/_list-group.scss","../node_modules/bootstrap/scss/_close.scss","../node_modules/bootstrap/scss/_toasts.scss","../node_modules/bootstrap/scss/_modal.scss","../node_modules/bootstrap/scss/mixins/_backdrop.scss","../node_modules/bootstrap/scss/_tooltip.scss","../node_modules/bootstrap/scss/mixins/_reset-text.scss","../node_modules/bootstrap/scss/_popover.scss","../node_modules/bootstrap/scss/_carousel.scss","../node_modules/bootstrap/scss/mixins/_clearfix.scss","../node_modules/bootstrap/scss/_spinners.scss","../node_modules/bootstrap/scss/_offcanvas.scss","../node_modules/bootstrap/scss/_placeholders.scss","../node_modules/bootstrap/scss/helpers/_colored-links.scss","../node_modules/bootstrap/scss/helpers/_ratio.scss","../node_modules/bootstrap/scss/helpers/_position.scss","../node_modules/bootstrap/scss/helpers/_stacks.scss","../node_modules/bootstrap/scss/helpers/_visually-hidden.scss","../node_modules/bootstrap/scss/mixins/_visually-hidden.scss","../node_modules/bootstrap/scss/helpers/_stretched-link.scss","../node_modules/bootstrap/scss/helpers/_text-truncation.scss","../node_modules/bootstrap/scss/mixins/_text-truncate.scss","../node_modules/bootstrap/scss/helpers/_vr.scss","../node_modules/bootstrap/scss/mixins/_utilities.scss","../node_modules/bootstrap/scss/utilities/_api.scss","App.css","../node_modules/react-clock/dist/Clock.css","../node_modules/react-time-picker/dist/TimePicker.css","../node_modules/react-datepicker/dist/react-datepicker.css","../node_modules/react-calendar/dist/Calendar.css","../node_modules/react-datetime-picker/dist/DateTimePicker.css"],"sourcesContent":["//\n// Headings\n//\n.h1 {\n @extend h1;\n}\n\n.h2 {\n @extend h2;\n}\n\n.h3 {\n @extend h3;\n}\n\n.h4 {\n @extend h4;\n}\n\n.h5 {\n @extend h5;\n}\n\n.h6 {\n @extend h6;\n}\n\n\n.lead {\n @include font-size($lead-font-size);\n font-weight: $lead-font-weight;\n}\n\n// Type display classes\n@each $display, $font-size in $display-font-sizes {\n .display-#{$display} {\n @include font-size($font-size);\n font-weight: $display-font-weight;\n line-height: $display-line-height;\n }\n}\n\n//\n// Emphasis\n//\n.small {\n @extend small;\n}\n\n.mark {\n @extend mark;\n}\n\n//\n// Lists\n//\n\n.list-unstyled {\n @include list-unstyled();\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n @include list-unstyled();\n}\n.list-inline-item {\n display: inline-block;\n\n &:not(:last-child) {\n margin-right: $list-inline-padding;\n }\n}\n\n\n//\n// Misc\n//\n\n// Builds on `abbr`\n.initialism {\n @include font-size($initialism-font-size);\n text-transform: uppercase;\n}\n\n// Blockquotes\n.blockquote {\n margin-bottom: $blockquote-margin-y;\n @include font-size($blockquote-font-size);\n\n > :last-child {\n margin-bottom: 0;\n }\n}\n\n.blockquote-footer {\n margin-top: -$blockquote-margin-y;\n margin-bottom: $blockquote-margin-y;\n @include font-size($blockquote-footer-font-size);\n color: $blockquote-footer-color;\n\n &::before {\n content: \"\\2014\\00A0\"; // em dash, nbsp\n }\n}\n","// Responsive images (ensure images don't scale beyond their parents)\n//\n// This is purposefully opt-in via an explicit class rather than being the default for all ``s.\n// We previously tried the \"images are responsive by default\" approach in Bootstrap v2,\n// and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps)\n// which weren't expecting the images within themselves to be involuntarily resized.\n// See also https://github.com/twbs/bootstrap/issues/18178\n.img-fluid {\n @include img-fluid();\n}\n\n\n// Image thumbnails\n.img-thumbnail {\n padding: $thumbnail-padding;\n background-color: $thumbnail-bg;\n border: $thumbnail-border-width solid $thumbnail-border-color;\n @include border-radius($thumbnail-border-radius);\n @include box-shadow($thumbnail-box-shadow);\n\n // Keep them at most 100% wide\n @include img-fluid();\n}\n\n//\n// Figures\n//\n\n.figure {\n // Ensures the caption's text aligns with the image.\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: $spacer * .5;\n line-height: 1;\n}\n\n.figure-caption {\n @include font-size($figure-caption-font-size);\n color: $figure-caption-color;\n}\n","body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n","/*!\n * Bootstrap v5.1.3 (https://getbootstrap.com/)\n * Copyright 2011-2021 The Bootstrap Authors\n * Copyright 2011-2021 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n\n// scss-docs-start import-stack\n// Configuration\n@import \"functions\";\n@import \"variables\";\n@import \"mixins\";\n@import \"utilities\";\n\n// Layout & components\n@import \"root\";\n@import \"reboot\";\n@import \"type\";\n@import \"images\";\n@import \"containers\";\n@import \"grid\";\n@import \"tables\";\n@import \"forms\";\n@import \"buttons\";\n@import \"transitions\";\n@import \"dropdown\";\n@import \"button-group\";\n@import \"nav\";\n@import \"navbar\";\n@import \"card\";\n@import \"accordion\";\n@import \"breadcrumb\";\n@import \"pagination\";\n@import \"badge\";\n@import \"alert\";\n@import \"progress\";\n@import \"list-group\";\n@import \"close\";\n@import \"toasts\";\n@import \"modal\";\n@import \"tooltip\";\n@import \"popover\";\n@import \"carousel\";\n@import \"spinners\";\n@import \"offcanvas\";\n@import \"placeholders\";\n\n// Helpers\n@import \"helpers\";\n\n// Utilities\n@import \"utilities/api\";\n// scss-docs-end import-stack\n",":root {\n // Note: Custom variable values only support SassScript inside `#{}`.\n\n // Colors\n //\n // Generate palettes for full colors, grays, and theme colors.\n\n @each $color, $value in $colors {\n --#{$variable-prefix}#{$color}: #{$value};\n }\n\n @each $color, $value in $grays {\n --#{$variable-prefix}gray-#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors {\n --#{$variable-prefix}#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors-rgb {\n --#{$variable-prefix}#{$color}-rgb: #{$value};\n }\n\n --#{$variable-prefix}white-rgb: #{to-rgb($white)};\n --#{$variable-prefix}black-rgb: #{to-rgb($black)};\n --#{$variable-prefix}body-color-rgb: #{to-rgb($body-color)};\n --#{$variable-prefix}body-bg-rgb: #{to-rgb($body-bg)};\n\n // Fonts\n\n // Note: Use `inspect` for lists so that quoted items keep the quotes.\n // See https://github.com/sass/sass/issues/2383#issuecomment-336349172\n --#{$variable-prefix}font-sans-serif: #{inspect($font-family-sans-serif)};\n --#{$variable-prefix}font-monospace: #{inspect($font-family-monospace)};\n --#{$variable-prefix}gradient: #{$gradient};\n\n // Root and body\n // stylelint-disable custom-property-empty-line-before\n // scss-docs-start root-body-variables\n @if $font-size-root != null {\n --#{$variable-prefix}root-font-size: #{$font-size-root};\n }\n --#{$variable-prefix}body-font-family: #{$font-family-base};\n --#{$variable-prefix}body-font-size: #{$font-size-base};\n --#{$variable-prefix}body-font-weight: #{$font-weight-base};\n --#{$variable-prefix}body-line-height: #{$line-height-base};\n --#{$variable-prefix}body-color: #{$body-color};\n @if $body-text-align != null {\n --#{$variable-prefix}body-text-align: #{$body-text-align};\n }\n --#{$variable-prefix}body-bg: #{$body-bg};\n // scss-docs-end root-body-variables\n // stylelint-enable custom-property-empty-line-before\n}\n","@charset \"UTF-8\";\n/*!\n * Bootstrap v5.1.3 (https://getbootstrap.com/)\n * Copyright 2011-2021 The Bootstrap Authors\n * Copyright 2011-2021 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n:root {\n --bs-blue: #0d6efd;\n --bs-indigo: #6610f2;\n --bs-purple: #6f42c1;\n --bs-pink: #d63384;\n --bs-red: #dc3545;\n --bs-orange: #fd7e14;\n --bs-yellow: #ffc107;\n --bs-green: #198754;\n --bs-teal: #20c997;\n --bs-cyan: #0dcaf0;\n --bs-white: #fff;\n --bs-gray: #6c757d;\n --bs-gray-dark: #343a40;\n --bs-gray-100: #f8f9fa;\n --bs-gray-200: #e9ecef;\n --bs-gray-300: #dee2e6;\n --bs-gray-400: #ced4da;\n --bs-gray-500: #adb5bd;\n --bs-gray-600: #6c757d;\n --bs-gray-700: #495057;\n --bs-gray-800: #343a40;\n --bs-gray-900: #212529;\n --bs-primary: #0d6efd;\n --bs-secondary: #6c757d;\n --bs-success: #198754;\n --bs-info: #0dcaf0;\n --bs-warning: #ffc107;\n --bs-danger: #dc3545;\n --bs-light: #f8f9fa;\n --bs-dark: #212529;\n --bs-primary-rgb: 13, 110, 253;\n --bs-secondary-rgb: 108, 117, 125;\n --bs-success-rgb: 25, 135, 84;\n --bs-info-rgb: 13, 202, 240;\n --bs-warning-rgb: 255, 193, 7;\n --bs-danger-rgb: 220, 53, 69;\n --bs-light-rgb: 248, 249, 250;\n --bs-dark-rgb: 33, 37, 41;\n --bs-white-rgb: 255, 255, 255;\n --bs-black-rgb: 0, 0, 0;\n --bs-body-color-rgb: 33, 37, 41;\n --bs-body-bg-rgb: 255, 255, 255;\n --bs-font-sans-serif: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", \"Liberation Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));\n --bs-body-font-family: var(--bs-font-sans-serif);\n --bs-body-font-size: 1rem;\n --bs-body-font-weight: 400;\n --bs-body-line-height: 1.5;\n --bs-body-color: #212529;\n --bs-body-bg: #fff;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n :root {\n scroll-behavior: smooth;\n }\n}\n\nbody {\n margin: 0;\n font-family: var(--bs-body-font-family);\n font-size: var(--bs-body-font-size);\n font-weight: var(--bs-body-font-weight);\n line-height: var(--bs-body-line-height);\n color: var(--bs-body-color);\n text-align: var(--bs-body-text-align);\n background-color: var(--bs-body-bg);\n -webkit-text-size-adjust: 100%;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\nhr {\n margin: 1rem 0;\n color: inherit;\n background-color: currentColor;\n border: 0;\n opacity: 0.25;\n}\n\nhr:not([size]) {\n height: 1px;\n}\n\nh6, .h6, h5, .h5, h4, .h4, h3, .h3, h2, .h2, h1, .h1 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n font-weight: 500;\n line-height: 1.2;\n}\n\nh1, .h1 {\n font-size: calc(1.375rem + 1.5vw);\n}\n@media (min-width: 1200px) {\n h1, .h1 {\n font-size: 2.5rem;\n }\n}\n\nh2, .h2 {\n font-size: calc(1.325rem + 0.9vw);\n}\n@media (min-width: 1200px) {\n h2, .h2 {\n font-size: 2rem;\n }\n}\n\nh3, .h3 {\n font-size: calc(1.3rem + 0.6vw);\n}\n@media (min-width: 1200px) {\n h3, .h3 {\n font-size: 1.75rem;\n }\n}\n\nh4, .h4 {\n font-size: calc(1.275rem + 0.3vw);\n}\n@media (min-width: 1200px) {\n h4, .h4 {\n font-size: 1.5rem;\n }\n}\n\nh5, .h5 {\n font-size: 1.25rem;\n}\n\nh6, .h6 {\n font-size: 1rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-bs-original-title] {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n cursor: help;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul {\n padding-left: 2rem;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: 0.5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall, .small {\n font-size: 0.875em;\n}\n\nmark, .mark {\n padding: 0.2em;\n background-color: #fcf8e3;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 0.75em;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\na {\n color: #0d6efd;\n text-decoration: underline;\n}\na:hover {\n color: #0a58ca;\n}\n\na:not([href]):not([class]), a:not([href]):not([class]):hover {\n color: inherit;\n text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: var(--bs-font-monospace);\n font-size: 1em;\n direction: ltr /* rtl:ignore */;\n unicode-bidi: bidi-override;\n}\n\npre {\n display: block;\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n font-size: 0.875em;\n}\npre code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n}\n\ncode {\n font-size: 0.875em;\n color: #d63384;\n word-wrap: break-word;\n}\na > code {\n color: inherit;\n}\n\nkbd {\n padding: 0.2rem 0.4rem;\n font-size: 0.875em;\n color: #fff;\n background-color: #212529;\n border-radius: 0.2rem;\n}\nkbd kbd {\n padding: 0;\n font-size: 1em;\n font-weight: 700;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg,\nsvg {\n vertical-align: middle;\n}\n\ntable {\n caption-side: bottom;\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n color: #6c757d;\n text-align: left;\n}\n\nth {\n text-align: inherit;\n text-align: -webkit-match-parent;\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n}\n\nlabel {\n display: inline-block;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus:not(:focus-visible) {\n outline: 0;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\n[role=button] {\n cursor: pointer;\n}\n\nselect {\n word-wrap: normal;\n}\nselect:disabled {\n opacity: 1;\n}\n\n[list]::-webkit-calendar-picker-indicator {\n display: none;\n}\n\nbutton,\n[type=button],\n[type=reset],\n[type=submit] {\n -webkit-appearance: button;\n}\nbutton:not(:disabled),\n[type=button]:not(:disabled),\n[type=reset]:not(:disabled),\n[type=submit]:not(:disabled) {\n cursor: pointer;\n}\n\n::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ntextarea {\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n float: left;\n width: 100%;\n padding: 0;\n margin-bottom: 0.5rem;\n font-size: calc(1.275rem + 0.3vw);\n line-height: inherit;\n}\n@media (min-width: 1200px) {\n legend {\n font-size: 1.5rem;\n }\n}\nlegend + * {\n clear: left;\n}\n\n::-webkit-datetime-edit-fields-wrapper,\n::-webkit-datetime-edit-text,\n::-webkit-datetime-edit-minute,\n::-webkit-datetime-edit-hour-field,\n::-webkit-datetime-edit-day-field,\n::-webkit-datetime-edit-month-field,\n::-webkit-datetime-edit-year-field {\n padding: 0;\n}\n\n::-webkit-inner-spin-button {\n height: auto;\n}\n\n[type=search] {\n outline-offset: -2px;\n -webkit-appearance: textfield;\n}\n\n/* rtl:raw:\n[type=\"tel\"],\n[type=\"url\"],\n[type=\"email\"],\n[type=\"number\"] {\n direction: ltr;\n}\n*/\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-color-swatch-wrapper {\n padding: 0;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n}\n\n::file-selector-button {\n font: inherit;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\niframe {\n border: 0;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[hidden] {\n display: none !important;\n}\n\n.lead {\n font-size: 1.25rem;\n font-weight: 300;\n}\n\n.display-1 {\n font-size: calc(1.625rem + 4.5vw);\n font-weight: 300;\n line-height: 1.2;\n}\n@media (min-width: 1200px) {\n .display-1 {\n font-size: 5rem;\n }\n}\n\n.display-2 {\n font-size: calc(1.575rem + 3.9vw);\n font-weight: 300;\n line-height: 1.2;\n}\n@media (min-width: 1200px) {\n .display-2 {\n font-size: 4.5rem;\n }\n}\n\n.display-3 {\n font-size: calc(1.525rem + 3.3vw);\n font-weight: 300;\n line-height: 1.2;\n}\n@media (min-width: 1200px) {\n .display-3 {\n font-size: 4rem;\n }\n}\n\n.display-4 {\n font-size: calc(1.475rem + 2.7vw);\n font-weight: 300;\n line-height: 1.2;\n}\n@media (min-width: 1200px) {\n .display-4 {\n font-size: 3.5rem;\n }\n}\n\n.display-5 {\n font-size: calc(1.425rem + 2.1vw);\n font-weight: 300;\n line-height: 1.2;\n}\n@media (min-width: 1200px) {\n .display-5 {\n font-size: 3rem;\n }\n}\n\n.display-6 {\n font-size: calc(1.375rem + 1.5vw);\n font-weight: 300;\n line-height: 1.2;\n}\n@media (min-width: 1200px) {\n .display-6 {\n font-size: 2.5rem;\n }\n}\n\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline-item {\n display: inline-block;\n}\n.list-inline-item:not(:last-child) {\n margin-right: 0.5rem;\n}\n\n.initialism {\n font-size: 0.875em;\n text-transform: uppercase;\n}\n\n.blockquote {\n margin-bottom: 1rem;\n font-size: 1.25rem;\n}\n.blockquote > :last-child {\n margin-bottom: 0;\n}\n\n.blockquote-footer {\n margin-top: -1rem;\n margin-bottom: 1rem;\n font-size: 0.875em;\n color: #6c757d;\n}\n.blockquote-footer::before {\n content: \"— \";\n}\n\n.img-fluid {\n max-width: 100%;\n height: auto;\n}\n\n.img-thumbnail {\n padding: 0.25rem;\n background-color: #fff;\n border: 1px solid #dee2e6;\n border-radius: 0.25rem;\n max-width: 100%;\n height: auto;\n}\n\n.figure {\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: 0.5rem;\n line-height: 1;\n}\n\n.figure-caption {\n font-size: 0.875em;\n color: #6c757d;\n}\n\n.container,\n.container-fluid,\n.container-xxl,\n.container-xl,\n.container-lg,\n.container-md,\n.container-sm {\n width: 100%;\n padding-right: var(--bs-gutter-x, 0.75rem);\n padding-left: var(--bs-gutter-x, 0.75rem);\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container-sm, .container {\n max-width: 540px;\n }\n}\n@media (min-width: 768px) {\n .container-md, .container-sm, .container {\n max-width: 720px;\n }\n}\n@media (min-width: 992px) {\n .container-lg, .container-md, .container-sm, .container {\n max-width: 960px;\n }\n}\n@media (min-width: 1200px) {\n .container-xl, .container-lg, .container-md, .container-sm, .container {\n max-width: 1140px;\n }\n}\n@media (min-width: 1400px) {\n .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container {\n max-width: 1320px;\n }\n}\n.row {\n --bs-gutter-x: 1.5rem;\n --bs-gutter-y: 0;\n display: flex;\n flex-wrap: wrap;\n margin-top: calc(-1 * var(--bs-gutter-y));\n margin-right: calc(-0.5 * var(--bs-gutter-x));\n margin-left: calc(-0.5 * var(--bs-gutter-x));\n}\n.row > * {\n flex-shrink: 0;\n width: 100%;\n max-width: 100%;\n padding-right: calc(var(--bs-gutter-x) * 0.5);\n padding-left: calc(var(--bs-gutter-x) * 0.5);\n margin-top: var(--bs-gutter-y);\n}\n\n.col {\n flex: 1 0 0%;\n}\n\n.row-cols-auto > * {\n flex: 0 0 auto;\n width: auto;\n}\n\n.row-cols-1 > * {\n flex: 0 0 auto;\n width: 100%;\n}\n\n.row-cols-2 > * {\n flex: 0 0 auto;\n width: 50%;\n}\n\n.row-cols-3 > * {\n flex: 0 0 auto;\n width: 33.3333333333%;\n}\n\n.row-cols-4 > * {\n flex: 0 0 auto;\n width: 25%;\n}\n\n.row-cols-5 > * {\n flex: 0 0 auto;\n width: 20%;\n}\n\n.row-cols-6 > * {\n flex: 0 0 auto;\n width: 16.6666666667%;\n}\n\n.col-auto {\n flex: 0 0 auto;\n width: auto;\n}\n\n.col-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n}\n\n.col-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n}\n\n.col-3 {\n flex: 0 0 auto;\n width: 25%;\n}\n\n.col-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n}\n\n.col-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n}\n\n.col-6 {\n flex: 0 0 auto;\n width: 50%;\n}\n\n.col-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n}\n\n.col-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n}\n\n.col-9 {\n flex: 0 0 auto;\n width: 75%;\n}\n\n.col-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n}\n\n.col-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n}\n\n.col-12 {\n flex: 0 0 auto;\n width: 100%;\n}\n\n.offset-1 {\n margin-left: 8.33333333%;\n}\n\n.offset-2 {\n margin-left: 16.66666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.33333333%;\n}\n\n.offset-5 {\n margin-left: 41.66666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.33333333%;\n}\n\n.offset-8 {\n margin-left: 66.66666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.33333333%;\n}\n\n.offset-11 {\n margin-left: 91.66666667%;\n}\n\n.g-0,\n.gx-0 {\n --bs-gutter-x: 0;\n}\n\n.g-0,\n.gy-0 {\n --bs-gutter-y: 0;\n}\n\n.g-1,\n.gx-1 {\n --bs-gutter-x: 0.25rem;\n}\n\n.g-1,\n.gy-1 {\n --bs-gutter-y: 0.25rem;\n}\n\n.g-2,\n.gx-2 {\n --bs-gutter-x: 0.5rem;\n}\n\n.g-2,\n.gy-2 {\n --bs-gutter-y: 0.5rem;\n}\n\n.g-3,\n.gx-3 {\n --bs-gutter-x: 1rem;\n}\n\n.g-3,\n.gy-3 {\n --bs-gutter-y: 1rem;\n}\n\n.g-4,\n.gx-4 {\n --bs-gutter-x: 1.5rem;\n}\n\n.g-4,\n.gy-4 {\n --bs-gutter-y: 1.5rem;\n}\n\n.g-5,\n.gx-5 {\n --bs-gutter-x: 3rem;\n}\n\n.g-5,\n.gy-5 {\n --bs-gutter-y: 3rem;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n flex: 1 0 0%;\n }\n\n .row-cols-sm-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n\n .row-cols-sm-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n\n .row-cols-sm-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n\n .row-cols-sm-3 > * {\n flex: 0 0 auto;\n width: 33.3333333333%;\n }\n\n .row-cols-sm-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n\n .row-cols-sm-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n\n .row-cols-sm-6 > * {\n flex: 0 0 auto;\n width: 16.6666666667%;\n }\n\n .col-sm-auto {\n flex: 0 0 auto;\n width: auto;\n }\n\n .col-sm-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n\n .col-sm-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n\n .col-sm-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n\n .col-sm-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n\n .col-sm-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n\n .col-sm-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n\n .col-sm-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n\n .col-sm-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n\n .col-sm-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n\n .col-sm-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n\n .col-sm-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n\n .col-sm-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n\n .offset-sm-0 {\n margin-left: 0;\n }\n\n .offset-sm-1 {\n margin-left: 8.33333333%;\n }\n\n .offset-sm-2 {\n margin-left: 16.66666667%;\n }\n\n .offset-sm-3 {\n margin-left: 25%;\n }\n\n .offset-sm-4 {\n margin-left: 33.33333333%;\n }\n\n .offset-sm-5 {\n margin-left: 41.66666667%;\n }\n\n .offset-sm-6 {\n margin-left: 50%;\n }\n\n .offset-sm-7 {\n margin-left: 58.33333333%;\n }\n\n .offset-sm-8 {\n margin-left: 66.66666667%;\n }\n\n .offset-sm-9 {\n margin-left: 75%;\n }\n\n .offset-sm-10 {\n margin-left: 83.33333333%;\n }\n\n .offset-sm-11 {\n margin-left: 91.66666667%;\n }\n\n .g-sm-0,\n.gx-sm-0 {\n --bs-gutter-x: 0;\n }\n\n .g-sm-0,\n.gy-sm-0 {\n --bs-gutter-y: 0;\n }\n\n .g-sm-1,\n.gx-sm-1 {\n --bs-gutter-x: 0.25rem;\n }\n\n .g-sm-1,\n.gy-sm-1 {\n --bs-gutter-y: 0.25rem;\n }\n\n .g-sm-2,\n.gx-sm-2 {\n --bs-gutter-x: 0.5rem;\n }\n\n .g-sm-2,\n.gy-sm-2 {\n --bs-gutter-y: 0.5rem;\n }\n\n .g-sm-3,\n.gx-sm-3 {\n --bs-gutter-x: 1rem;\n }\n\n .g-sm-3,\n.gy-sm-3 {\n --bs-gutter-y: 1rem;\n }\n\n .g-sm-4,\n.gx-sm-4 {\n --bs-gutter-x: 1.5rem;\n }\n\n .g-sm-4,\n.gy-sm-4 {\n --bs-gutter-y: 1.5rem;\n }\n\n .g-sm-5,\n.gx-sm-5 {\n --bs-gutter-x: 3rem;\n }\n\n .g-sm-5,\n.gy-sm-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 768px) {\n .col-md {\n flex: 1 0 0%;\n }\n\n .row-cols-md-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n\n .row-cols-md-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n\n .row-cols-md-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n\n .row-cols-md-3 > * {\n flex: 0 0 auto;\n width: 33.3333333333%;\n }\n\n .row-cols-md-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n\n .row-cols-md-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n\n .row-cols-md-6 > * {\n flex: 0 0 auto;\n width: 16.6666666667%;\n }\n\n .col-md-auto {\n flex: 0 0 auto;\n width: auto;\n }\n\n .col-md-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n\n .col-md-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n\n .col-md-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n\n .col-md-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n\n .col-md-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n\n .col-md-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n\n .col-md-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n\n .col-md-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n\n .col-md-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n\n .col-md-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n\n .col-md-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n\n .col-md-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n\n .offset-md-0 {\n margin-left: 0;\n }\n\n .offset-md-1 {\n margin-left: 8.33333333%;\n }\n\n .offset-md-2 {\n margin-left: 16.66666667%;\n }\n\n .offset-md-3 {\n margin-left: 25%;\n }\n\n .offset-md-4 {\n margin-left: 33.33333333%;\n }\n\n .offset-md-5 {\n margin-left: 41.66666667%;\n }\n\n .offset-md-6 {\n margin-left: 50%;\n }\n\n .offset-md-7 {\n margin-left: 58.33333333%;\n }\n\n .offset-md-8 {\n margin-left: 66.66666667%;\n }\n\n .offset-md-9 {\n margin-left: 75%;\n }\n\n .offset-md-10 {\n margin-left: 83.33333333%;\n }\n\n .offset-md-11 {\n margin-left: 91.66666667%;\n }\n\n .g-md-0,\n.gx-md-0 {\n --bs-gutter-x: 0;\n }\n\n .g-md-0,\n.gy-md-0 {\n --bs-gutter-y: 0;\n }\n\n .g-md-1,\n.gx-md-1 {\n --bs-gutter-x: 0.25rem;\n }\n\n .g-md-1,\n.gy-md-1 {\n --bs-gutter-y: 0.25rem;\n }\n\n .g-md-2,\n.gx-md-2 {\n --bs-gutter-x: 0.5rem;\n }\n\n .g-md-2,\n.gy-md-2 {\n --bs-gutter-y: 0.5rem;\n }\n\n .g-md-3,\n.gx-md-3 {\n --bs-gutter-x: 1rem;\n }\n\n .g-md-3,\n.gy-md-3 {\n --bs-gutter-y: 1rem;\n }\n\n .g-md-4,\n.gx-md-4 {\n --bs-gutter-x: 1.5rem;\n }\n\n .g-md-4,\n.gy-md-4 {\n --bs-gutter-y: 1.5rem;\n }\n\n .g-md-5,\n.gx-md-5 {\n --bs-gutter-x: 3rem;\n }\n\n .g-md-5,\n.gy-md-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 992px) {\n .col-lg {\n flex: 1 0 0%;\n }\n\n .row-cols-lg-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n\n .row-cols-lg-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n\n .row-cols-lg-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n\n .row-cols-lg-3 > * {\n flex: 0 0 auto;\n width: 33.3333333333%;\n }\n\n .row-cols-lg-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n\n .row-cols-lg-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n\n .row-cols-lg-6 > * {\n flex: 0 0 auto;\n width: 16.6666666667%;\n }\n\n .col-lg-auto {\n flex: 0 0 auto;\n width: auto;\n }\n\n .col-lg-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n\n .col-lg-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n\n .col-lg-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n\n .col-lg-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n\n .col-lg-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n\n .col-lg-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n\n .col-lg-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n\n .col-lg-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n\n .col-lg-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n\n .col-lg-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n\n .col-lg-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n\n .col-lg-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n\n .offset-lg-0 {\n margin-left: 0;\n }\n\n .offset-lg-1 {\n margin-left: 8.33333333%;\n }\n\n .offset-lg-2 {\n margin-left: 16.66666667%;\n }\n\n .offset-lg-3 {\n margin-left: 25%;\n }\n\n .offset-lg-4 {\n margin-left: 33.33333333%;\n }\n\n .offset-lg-5 {\n margin-left: 41.66666667%;\n }\n\n .offset-lg-6 {\n margin-left: 50%;\n }\n\n .offset-lg-7 {\n margin-left: 58.33333333%;\n }\n\n .offset-lg-8 {\n margin-left: 66.66666667%;\n }\n\n .offset-lg-9 {\n margin-left: 75%;\n }\n\n .offset-lg-10 {\n margin-left: 83.33333333%;\n }\n\n .offset-lg-11 {\n margin-left: 91.66666667%;\n }\n\n .g-lg-0,\n.gx-lg-0 {\n --bs-gutter-x: 0;\n }\n\n .g-lg-0,\n.gy-lg-0 {\n --bs-gutter-y: 0;\n }\n\n .g-lg-1,\n.gx-lg-1 {\n --bs-gutter-x: 0.25rem;\n }\n\n .g-lg-1,\n.gy-lg-1 {\n --bs-gutter-y: 0.25rem;\n }\n\n .g-lg-2,\n.gx-lg-2 {\n --bs-gutter-x: 0.5rem;\n }\n\n .g-lg-2,\n.gy-lg-2 {\n --bs-gutter-y: 0.5rem;\n }\n\n .g-lg-3,\n.gx-lg-3 {\n --bs-gutter-x: 1rem;\n }\n\n .g-lg-3,\n.gy-lg-3 {\n --bs-gutter-y: 1rem;\n }\n\n .g-lg-4,\n.gx-lg-4 {\n --bs-gutter-x: 1.5rem;\n }\n\n .g-lg-4,\n.gy-lg-4 {\n --bs-gutter-y: 1.5rem;\n }\n\n .g-lg-5,\n.gx-lg-5 {\n --bs-gutter-x: 3rem;\n }\n\n .g-lg-5,\n.gy-lg-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 1200px) {\n .col-xl {\n flex: 1 0 0%;\n }\n\n .row-cols-xl-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n\n .row-cols-xl-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n\n .row-cols-xl-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n\n .row-cols-xl-3 > * {\n flex: 0 0 auto;\n width: 33.3333333333%;\n }\n\n .row-cols-xl-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n\n .row-cols-xl-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n\n .row-cols-xl-6 > * {\n flex: 0 0 auto;\n width: 16.6666666667%;\n }\n\n .col-xl-auto {\n flex: 0 0 auto;\n width: auto;\n }\n\n .col-xl-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n\n .col-xl-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n\n .col-xl-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n\n .col-xl-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n\n .col-xl-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n\n .col-xl-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n\n .col-xl-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n\n .col-xl-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n\n .col-xl-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n\n .col-xl-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n\n .col-xl-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n\n .col-xl-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n\n .offset-xl-0 {\n margin-left: 0;\n }\n\n .offset-xl-1 {\n margin-left: 8.33333333%;\n }\n\n .offset-xl-2 {\n margin-left: 16.66666667%;\n }\n\n .offset-xl-3 {\n margin-left: 25%;\n }\n\n .offset-xl-4 {\n margin-left: 33.33333333%;\n }\n\n .offset-xl-5 {\n margin-left: 41.66666667%;\n }\n\n .offset-xl-6 {\n margin-left: 50%;\n }\n\n .offset-xl-7 {\n margin-left: 58.33333333%;\n }\n\n .offset-xl-8 {\n margin-left: 66.66666667%;\n }\n\n .offset-xl-9 {\n margin-left: 75%;\n }\n\n .offset-xl-10 {\n margin-left: 83.33333333%;\n }\n\n .offset-xl-11 {\n margin-left: 91.66666667%;\n }\n\n .g-xl-0,\n.gx-xl-0 {\n --bs-gutter-x: 0;\n }\n\n .g-xl-0,\n.gy-xl-0 {\n --bs-gutter-y: 0;\n }\n\n .g-xl-1,\n.gx-xl-1 {\n --bs-gutter-x: 0.25rem;\n }\n\n .g-xl-1,\n.gy-xl-1 {\n --bs-gutter-y: 0.25rem;\n }\n\n .g-xl-2,\n.gx-xl-2 {\n --bs-gutter-x: 0.5rem;\n }\n\n .g-xl-2,\n.gy-xl-2 {\n --bs-gutter-y: 0.5rem;\n }\n\n .g-xl-3,\n.gx-xl-3 {\n --bs-gutter-x: 1rem;\n }\n\n .g-xl-3,\n.gy-xl-3 {\n --bs-gutter-y: 1rem;\n }\n\n .g-xl-4,\n.gx-xl-4 {\n --bs-gutter-x: 1.5rem;\n }\n\n .g-xl-4,\n.gy-xl-4 {\n --bs-gutter-y: 1.5rem;\n }\n\n .g-xl-5,\n.gx-xl-5 {\n --bs-gutter-x: 3rem;\n }\n\n .g-xl-5,\n.gy-xl-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 1400px) {\n .col-xxl {\n flex: 1 0 0%;\n }\n\n .row-cols-xxl-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n\n .row-cols-xxl-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n\n .row-cols-xxl-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n\n .row-cols-xxl-3 > * {\n flex: 0 0 auto;\n width: 33.3333333333%;\n }\n\n .row-cols-xxl-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n\n .row-cols-xxl-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n\n .row-cols-xxl-6 > * {\n flex: 0 0 auto;\n width: 16.6666666667%;\n }\n\n .col-xxl-auto {\n flex: 0 0 auto;\n width: auto;\n }\n\n .col-xxl-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n\n .col-xxl-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n\n .col-xxl-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n\n .col-xxl-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n\n .col-xxl-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n\n .col-xxl-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n\n .col-xxl-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n\n .col-xxl-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n\n .col-xxl-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n\n .col-xxl-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n\n .col-xxl-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n\n .col-xxl-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n\n .offset-xxl-0 {\n margin-left: 0;\n }\n\n .offset-xxl-1 {\n margin-left: 8.33333333%;\n }\n\n .offset-xxl-2 {\n margin-left: 16.66666667%;\n }\n\n .offset-xxl-3 {\n margin-left: 25%;\n }\n\n .offset-xxl-4 {\n margin-left: 33.33333333%;\n }\n\n .offset-xxl-5 {\n margin-left: 41.66666667%;\n }\n\n .offset-xxl-6 {\n margin-left: 50%;\n }\n\n .offset-xxl-7 {\n margin-left: 58.33333333%;\n }\n\n .offset-xxl-8 {\n margin-left: 66.66666667%;\n }\n\n .offset-xxl-9 {\n margin-left: 75%;\n }\n\n .offset-xxl-10 {\n margin-left: 83.33333333%;\n }\n\n .offset-xxl-11 {\n margin-left: 91.66666667%;\n }\n\n .g-xxl-0,\n.gx-xxl-0 {\n --bs-gutter-x: 0;\n }\n\n .g-xxl-0,\n.gy-xxl-0 {\n --bs-gutter-y: 0;\n }\n\n .g-xxl-1,\n.gx-xxl-1 {\n --bs-gutter-x: 0.25rem;\n }\n\n .g-xxl-1,\n.gy-xxl-1 {\n --bs-gutter-y: 0.25rem;\n }\n\n .g-xxl-2,\n.gx-xxl-2 {\n --bs-gutter-x: 0.5rem;\n }\n\n .g-xxl-2,\n.gy-xxl-2 {\n --bs-gutter-y: 0.5rem;\n }\n\n .g-xxl-3,\n.gx-xxl-3 {\n --bs-gutter-x: 1rem;\n }\n\n .g-xxl-3,\n.gy-xxl-3 {\n --bs-gutter-y: 1rem;\n }\n\n .g-xxl-4,\n.gx-xxl-4 {\n --bs-gutter-x: 1.5rem;\n }\n\n .g-xxl-4,\n.gy-xxl-4 {\n --bs-gutter-y: 1.5rem;\n }\n\n .g-xxl-5,\n.gx-xxl-5 {\n --bs-gutter-x: 3rem;\n }\n\n .g-xxl-5,\n.gy-xxl-5 {\n --bs-gutter-y: 3rem;\n }\n}\n.table {\n --bs-table-bg: transparent;\n --bs-table-accent-bg: transparent;\n --bs-table-striped-color: #212529;\n --bs-table-striped-bg: rgba(0, 0, 0, 0.05);\n --bs-table-active-color: #212529;\n --bs-table-active-bg: rgba(0, 0, 0, 0.1);\n --bs-table-hover-color: #212529;\n --bs-table-hover-bg: rgba(0, 0, 0, 0.075);\n width: 100%;\n margin-bottom: 1rem;\n color: #212529;\n vertical-align: top;\n border-color: #dee2e6;\n}\n.table > :not(caption) > * > * {\n padding: 0.5rem 0.5rem;\n background-color: var(--bs-table-bg);\n border-bottom-width: 1px;\n box-shadow: inset 0 0 0 9999px var(--bs-table-accent-bg);\n}\n.table > tbody {\n vertical-align: inherit;\n}\n.table > thead {\n vertical-align: bottom;\n}\n.table > :not(:first-child) {\n border-top: 2px solid currentColor;\n}\n\n.caption-top {\n caption-side: top;\n}\n\n.table-sm > :not(caption) > * > * {\n padding: 0.25rem 0.25rem;\n}\n\n.table-bordered > :not(caption) > * {\n border-width: 1px 0;\n}\n.table-bordered > :not(caption) > * > * {\n border-width: 0 1px;\n}\n\n.table-borderless > :not(caption) > * > * {\n border-bottom-width: 0;\n}\n.table-borderless > :not(:first-child) {\n border-top-width: 0;\n}\n\n.table-striped > tbody > tr:nth-of-type(odd) > * {\n --bs-table-accent-bg: var(--bs-table-striped-bg);\n color: var(--bs-table-striped-color);\n}\n\n.table-active {\n --bs-table-accent-bg: var(--bs-table-active-bg);\n color: var(--bs-table-active-color);\n}\n\n.table-hover > tbody > tr:hover > * {\n --bs-table-accent-bg: var(--bs-table-hover-bg);\n color: var(--bs-table-hover-color);\n}\n\n.table-primary {\n --bs-table-bg: #cfe2ff;\n --bs-table-striped-bg: #c5d7f2;\n --bs-table-striped-color: #000;\n --bs-table-active-bg: #bacbe6;\n --bs-table-active-color: #000;\n --bs-table-hover-bg: #bfd1ec;\n --bs-table-hover-color: #000;\n color: #000;\n border-color: #bacbe6;\n}\n\n.table-secondary {\n --bs-table-bg: #e2e3e5;\n --bs-table-striped-bg: #d7d8da;\n --bs-table-striped-color: #000;\n --bs-table-active-bg: #cbccce;\n --bs-table-active-color: #000;\n --bs-table-hover-bg: #d1d2d4;\n --bs-table-hover-color: #000;\n color: #000;\n border-color: #cbccce;\n}\n\n.table-success {\n --bs-table-bg: #d1e7dd;\n --bs-table-striped-bg: #c7dbd2;\n --bs-table-striped-color: #000;\n --bs-table-active-bg: #bcd0c7;\n --bs-table-active-color: #000;\n --bs-table-hover-bg: #c1d6cc;\n --bs-table-hover-color: #000;\n color: #000;\n border-color: #bcd0c7;\n}\n\n.table-info {\n --bs-table-bg: #cff4fc;\n --bs-table-striped-bg: #c5e8ef;\n --bs-table-striped-color: #000;\n --bs-table-active-bg: #badce3;\n --bs-table-active-color: #000;\n --bs-table-hover-bg: #bfe2e9;\n --bs-table-hover-color: #000;\n color: #000;\n border-color: #badce3;\n}\n\n.table-warning {\n --bs-table-bg: #fff3cd;\n --bs-table-striped-bg: #f2e7c3;\n --bs-table-striped-color: #000;\n --bs-table-active-bg: #e6dbb9;\n --bs-table-active-color: #000;\n --bs-table-hover-bg: #ece1be;\n --bs-table-hover-color: #000;\n color: #000;\n border-color: #e6dbb9;\n}\n\n.table-danger {\n --bs-table-bg: #f8d7da;\n --bs-table-striped-bg: #eccccf;\n --bs-table-striped-color: #000;\n --bs-table-active-bg: #dfc2c4;\n --bs-table-active-color: #000;\n --bs-table-hover-bg: #e5c7ca;\n --bs-table-hover-color: #000;\n color: #000;\n border-color: #dfc2c4;\n}\n\n.table-light {\n --bs-table-bg: #f8f9fa;\n --bs-table-striped-bg: #ecedee;\n --bs-table-striped-color: #000;\n --bs-table-active-bg: #dfe0e1;\n --bs-table-active-color: #000;\n --bs-table-hover-bg: #e5e6e7;\n --bs-table-hover-color: #000;\n color: #000;\n border-color: #dfe0e1;\n}\n\n.table-dark {\n --bs-table-bg: #212529;\n --bs-table-striped-bg: #2c3034;\n --bs-table-striped-color: #fff;\n --bs-table-active-bg: #373b3e;\n --bs-table-active-color: #fff;\n --bs-table-hover-bg: #323539;\n --bs-table-hover-color: #fff;\n color: #fff;\n border-color: #373b3e;\n}\n\n.table-responsive {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n}\n\n@media (max-width: 575.98px) {\n .table-responsive-sm {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n}\n@media (max-width: 767.98px) {\n .table-responsive-md {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n}\n@media (max-width: 991.98px) {\n .table-responsive-lg {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n}\n@media (max-width: 1199.98px) {\n .table-responsive-xl {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n}\n@media (max-width: 1399.98px) {\n .table-responsive-xxl {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n}\n.form-label {\n margin-bottom: 0.5rem;\n}\n\n.col-form-label {\n padding-top: calc(0.375rem + 1px);\n padding-bottom: calc(0.375rem + 1px);\n margin-bottom: 0;\n font-size: inherit;\n line-height: 1.5;\n}\n\n.col-form-label-lg {\n padding-top: calc(0.5rem + 1px);\n padding-bottom: calc(0.5rem + 1px);\n font-size: 1.25rem;\n}\n\n.col-form-label-sm {\n padding-top: calc(0.25rem + 1px);\n padding-bottom: calc(0.25rem + 1px);\n font-size: 0.875rem;\n}\n\n.form-text {\n margin-top: 0.25rem;\n font-size: 0.875em;\n color: #6c757d;\n}\n\n.form-control {\n display: block;\n width: 100%;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ced4da;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n border-radius: 0.25rem;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .form-control {\n transition: none;\n }\n}\n.form-control[type=file] {\n overflow: hidden;\n}\n.form-control[type=file]:not(:disabled):not([readonly]) {\n cursor: pointer;\n}\n.form-control:focus {\n color: #212529;\n background-color: #fff;\n border-color: #86b7fe;\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-control::-webkit-date-and-time-value {\n height: 1.5em;\n}\n.form-control::-moz-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n.form-control::placeholder {\n color: #6c757d;\n opacity: 1;\n}\n.form-control:disabled, .form-control[readonly] {\n background-color: #e9ecef;\n opacity: 1;\n}\n.form-control::-webkit-file-upload-button {\n padding: 0.375rem 0.75rem;\n margin: -0.375rem -0.75rem;\n -webkit-margin-end: 0.75rem;\n margin-inline-end: 0.75rem;\n color: #212529;\n background-color: #e9ecef;\n pointer-events: none;\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n border-inline-end-width: 1px;\n border-radius: 0;\n -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n.form-control::file-selector-button {\n padding: 0.375rem 0.75rem;\n margin: -0.375rem -0.75rem;\n -webkit-margin-end: 0.75rem;\n margin-inline-end: 0.75rem;\n color: #212529;\n background-color: #e9ecef;\n pointer-events: none;\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n border-inline-end-width: 1px;\n border-radius: 0;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .form-control::-webkit-file-upload-button {\n -webkit-transition: none;\n transition: none;\n }\n .form-control::file-selector-button {\n transition: none;\n }\n}\n.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button {\n background-color: #dde0e3;\n}\n.form-control:hover:not(:disabled):not([readonly])::file-selector-button {\n background-color: #dde0e3;\n}\n.form-control::-webkit-file-upload-button {\n padding: 0.375rem 0.75rem;\n margin: -0.375rem -0.75rem;\n -webkit-margin-end: 0.75rem;\n margin-inline-end: 0.75rem;\n color: #212529;\n background-color: #e9ecef;\n pointer-events: none;\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n border-inline-end-width: 1px;\n border-radius: 0;\n -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .form-control::-webkit-file-upload-button {\n -webkit-transition: none;\n transition: none;\n }\n}\n.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button {\n background-color: #dde0e3;\n}\n\n.form-control-plaintext {\n display: block;\n width: 100%;\n padding: 0.375rem 0;\n margin-bottom: 0;\n line-height: 1.5;\n color: #212529;\n background-color: transparent;\n border: solid transparent;\n border-width: 1px 0;\n}\n.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg {\n padding-right: 0;\n padding-left: 0;\n}\n\n.form-control-sm {\n min-height: calc(1.5em + 0.5rem + 2px);\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n border-radius: 0.2rem;\n}\n.form-control-sm::-webkit-file-upload-button {\n padding: 0.25rem 0.5rem;\n margin: -0.25rem -0.5rem;\n -webkit-margin-end: 0.5rem;\n margin-inline-end: 0.5rem;\n}\n.form-control-sm::file-selector-button {\n padding: 0.25rem 0.5rem;\n margin: -0.25rem -0.5rem;\n -webkit-margin-end: 0.5rem;\n margin-inline-end: 0.5rem;\n}\n.form-control-sm::-webkit-file-upload-button {\n padding: 0.25rem 0.5rem;\n margin: -0.25rem -0.5rem;\n -webkit-margin-end: 0.5rem;\n margin-inline-end: 0.5rem;\n}\n\n.form-control-lg {\n min-height: calc(1.5em + 1rem + 2px);\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n border-radius: 0.3rem;\n}\n.form-control-lg::-webkit-file-upload-button {\n padding: 0.5rem 1rem;\n margin: -0.5rem -1rem;\n -webkit-margin-end: 1rem;\n margin-inline-end: 1rem;\n}\n.form-control-lg::file-selector-button {\n padding: 0.5rem 1rem;\n margin: -0.5rem -1rem;\n -webkit-margin-end: 1rem;\n margin-inline-end: 1rem;\n}\n.form-control-lg::-webkit-file-upload-button {\n padding: 0.5rem 1rem;\n margin: -0.5rem -1rem;\n -webkit-margin-end: 1rem;\n margin-inline-end: 1rem;\n}\n\ntextarea.form-control {\n min-height: calc(1.5em + 0.75rem + 2px);\n}\ntextarea.form-control-sm {\n min-height: calc(1.5em + 0.5rem + 2px);\n}\ntextarea.form-control-lg {\n min-height: calc(1.5em + 1rem + 2px);\n}\n\n.form-control-color {\n width: 3rem;\n height: auto;\n padding: 0.375rem;\n}\n.form-control-color:not(:disabled):not([readonly]) {\n cursor: pointer;\n}\n.form-control-color::-moz-color-swatch {\n height: 1.5em;\n border-radius: 0.25rem;\n}\n.form-control-color::-webkit-color-swatch {\n height: 1.5em;\n border-radius: 0.25rem;\n}\n\n.form-select {\n display: block;\n width: 100%;\n padding: 0.375rem 2.25rem 0.375rem 0.75rem;\n -moz-padding-start: calc(0.75rem - 3px);\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n background-color: #fff;\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e\");\n background-repeat: no-repeat;\n background-position: right 0.75rem center;\n background-size: 16px 12px;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n@media (prefers-reduced-motion: reduce) {\n .form-select {\n transition: none;\n }\n}\n.form-select:focus {\n border-color: #86b7fe;\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-select[multiple], .form-select[size]:not([size=\"1\"]) {\n padding-right: 0.75rem;\n background-image: none;\n}\n.form-select:disabled {\n background-color: #e9ecef;\n}\n.form-select:-moz-focusring {\n color: transparent;\n text-shadow: 0 0 0 #212529;\n}\n\n.form-select-sm {\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n padding-left: 0.5rem;\n font-size: 0.875rem;\n border-radius: 0.2rem;\n}\n\n.form-select-lg {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n font-size: 1.25rem;\n border-radius: 0.3rem;\n}\n\n.form-check {\n display: block;\n min-height: 1.5rem;\n padding-left: 1.5em;\n margin-bottom: 0.125rem;\n}\n.form-check .form-check-input {\n float: left;\n margin-left: -1.5em;\n}\n\n.form-check-input {\n width: 1em;\n height: 1em;\n margin-top: 0.25em;\n vertical-align: top;\n background-color: #fff;\n background-repeat: no-repeat;\n background-position: center;\n background-size: contain;\n border: 1px solid rgba(0, 0, 0, 0.25);\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n -webkit-print-color-adjust: exact;\n color-adjust: exact;\n}\n.form-check-input[type=checkbox] {\n border-radius: 0.25em;\n}\n.form-check-input[type=radio] {\n border-radius: 50%;\n}\n.form-check-input:active {\n filter: brightness(90%);\n}\n.form-check-input:focus {\n border-color: #86b7fe;\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-check-input:checked {\n background-color: #0d6efd;\n border-color: #0d6efd;\n}\n.form-check-input:checked[type=checkbox] {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e\");\n}\n.form-check-input:checked[type=radio] {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e\");\n}\n.form-check-input[type=checkbox]:indeterminate {\n background-color: #0d6efd;\n border-color: #0d6efd;\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e\");\n}\n.form-check-input:disabled {\n pointer-events: none;\n filter: none;\n opacity: 0.5;\n}\n.form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label {\n opacity: 0.5;\n}\n\n.form-switch {\n padding-left: 2.5em;\n}\n.form-switch .form-check-input {\n width: 2em;\n margin-left: -2.5em;\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e\");\n background-position: left center;\n border-radius: 2em;\n transition: background-position 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .form-switch .form-check-input {\n transition: none;\n }\n}\n.form-switch .form-check-input:focus {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e\");\n}\n.form-switch .form-check-input:checked {\n background-position: right center;\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e\");\n}\n\n.form-check-inline {\n display: inline-block;\n margin-right: 1rem;\n}\n\n.btn-check {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n.btn-check[disabled] + .btn, .btn-check:disabled + .btn {\n pointer-events: none;\n filter: none;\n opacity: 0.65;\n}\n\n.form-range {\n width: 100%;\n height: 1.5rem;\n padding: 0;\n background-color: transparent;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n.form-range:focus {\n outline: 0;\n}\n.form-range:focus::-webkit-slider-thumb {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-range:focus::-moz-range-thumb {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-range::-moz-focus-outer {\n border: 0;\n}\n.form-range::-webkit-slider-thumb {\n width: 1rem;\n height: 1rem;\n margin-top: -0.25rem;\n background-color: #0d6efd;\n border: 0;\n border-radius: 1rem;\n -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n -webkit-appearance: none;\n appearance: none;\n}\n@media (prefers-reduced-motion: reduce) {\n .form-range::-webkit-slider-thumb {\n -webkit-transition: none;\n transition: none;\n }\n}\n.form-range::-webkit-slider-thumb:active {\n background-color: #b6d4fe;\n}\n.form-range::-webkit-slider-runnable-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n.form-range::-moz-range-thumb {\n width: 1rem;\n height: 1rem;\n background-color: #0d6efd;\n border: 0;\n border-radius: 1rem;\n -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n -moz-appearance: none;\n appearance: none;\n}\n@media (prefers-reduced-motion: reduce) {\n .form-range::-moz-range-thumb {\n -moz-transition: none;\n transition: none;\n }\n}\n.form-range::-moz-range-thumb:active {\n background-color: #b6d4fe;\n}\n.form-range::-moz-range-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n.form-range:disabled {\n pointer-events: none;\n}\n.form-range:disabled::-webkit-slider-thumb {\n background-color: #adb5bd;\n}\n.form-range:disabled::-moz-range-thumb {\n background-color: #adb5bd;\n}\n\n.form-floating {\n position: relative;\n}\n.form-floating > .form-control,\n.form-floating > .form-select {\n height: calc(3.5rem + 2px);\n line-height: 1.25;\n}\n.form-floating > label {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n padding: 1rem 0.75rem;\n pointer-events: none;\n border: 1px solid transparent;\n transform-origin: 0 0;\n transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .form-floating > label {\n transition: none;\n }\n}\n.form-floating > .form-control {\n padding: 1rem 0.75rem;\n}\n.form-floating > .form-control::-moz-placeholder {\n color: transparent;\n}\n.form-floating > .form-control::placeholder {\n color: transparent;\n}\n.form-floating > .form-control:not(:-moz-placeholder-shown) {\n padding-top: 1.625rem;\n padding-bottom: 0.625rem;\n}\n.form-floating > .form-control:focus, .form-floating > .form-control:not(:placeholder-shown) {\n padding-top: 1.625rem;\n padding-bottom: 0.625rem;\n}\n.form-floating > .form-control:-webkit-autofill {\n padding-top: 1.625rem;\n padding-bottom: 0.625rem;\n}\n.form-floating > .form-select {\n padding-top: 1.625rem;\n padding-bottom: 0.625rem;\n}\n.form-floating > .form-control:not(:-moz-placeholder-shown) ~ label {\n opacity: 0.65;\n transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);\n}\n.form-floating > .form-control:focus ~ label,\n.form-floating > .form-control:not(:placeholder-shown) ~ label,\n.form-floating > .form-select ~ label {\n opacity: 0.65;\n transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);\n}\n.form-floating > .form-control:-webkit-autofill ~ label {\n opacity: 0.65;\n transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);\n}\n\n.input-group {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: stretch;\n width: 100%;\n}\n.input-group > .form-control,\n.input-group > .form-select {\n position: relative;\n flex: 1 1 auto;\n width: 1%;\n min-width: 0;\n}\n.input-group > .form-control:focus,\n.input-group > .form-select:focus {\n z-index: 3;\n}\n.input-group .btn {\n position: relative;\n z-index: 2;\n}\n.input-group .btn:focus {\n z-index: 3;\n}\n\n.input-group-text {\n display: flex;\n align-items: center;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: center;\n white-space: nowrap;\n background-color: #e9ecef;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.input-group-lg > .form-control,\n.input-group-lg > .form-select,\n.input-group-lg > .input-group-text,\n.input-group-lg > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n border-radius: 0.3rem;\n}\n\n.input-group-sm > .form-control,\n.input-group-sm > .form-select,\n.input-group-sm > .input-group-text,\n.input-group-sm > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n border-radius: 0.2rem;\n}\n\n.input-group-lg > .form-select,\n.input-group-sm > .form-select {\n padding-right: 3rem;\n}\n\n.input-group:not(.has-validation) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu),\n.input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n+3) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n.input-group.has-validation > :nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu),\n.input-group.has-validation > .dropdown-toggle:nth-last-child(n+4) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n.input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) {\n margin-left: -1px;\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.valid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 0.875em;\n color: #198754;\n}\n\n.valid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: 0.25rem 0.5rem;\n margin-top: 0.1rem;\n font-size: 0.875rem;\n color: #fff;\n background-color: rgba(25, 135, 84, 0.9);\n border-radius: 0.25rem;\n}\n\n.was-validated :valid ~ .valid-feedback,\n.was-validated :valid ~ .valid-tooltip,\n.is-valid ~ .valid-feedback,\n.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid {\n border-color: #198754;\n padding-right: calc(1.5em + 0.75rem);\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n background-repeat: no-repeat;\n background-position: right calc(0.375em + 0.1875rem) center;\n background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus {\n border-color: #198754;\n box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);\n}\n\n.was-validated textarea.form-control:valid, textarea.form-control.is-valid {\n padding-right: calc(1.5em + 0.75rem);\n background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .form-select:valid, .form-select.is-valid {\n border-color: #198754;\n}\n.was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size=\"1\"], .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid:not([multiple])[size=\"1\"] {\n padding-right: 4.125rem;\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e\"), url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n background-position: right 0.75rem center, center right 2.25rem;\n background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-select:valid:focus, .form-select.is-valid:focus {\n border-color: #198754;\n box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);\n}\n\n.was-validated .form-check-input:valid, .form-check-input.is-valid {\n border-color: #198754;\n}\n.was-validated .form-check-input:valid:checked, .form-check-input.is-valid:checked {\n background-color: #198754;\n}\n.was-validated .form-check-input:valid:focus, .form-check-input.is-valid:focus {\n box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);\n}\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n color: #198754;\n}\n\n.form-check-inline .form-check-input ~ .valid-feedback {\n margin-left: 0.5em;\n}\n\n.was-validated .input-group .form-control:valid, .input-group .form-control.is-valid,\n.was-validated .input-group .form-select:valid,\n.input-group .form-select.is-valid {\n z-index: 1;\n}\n.was-validated .input-group .form-control:valid:focus, .input-group .form-control.is-valid:focus,\n.was-validated .input-group .form-select:valid:focus,\n.input-group .form-select.is-valid:focus {\n z-index: 3;\n}\n\n.invalid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 0.875em;\n color: #dc3545;\n}\n\n.invalid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: 0.25rem 0.5rem;\n margin-top: 0.1rem;\n font-size: 0.875rem;\n color: #fff;\n background-color: rgba(220, 53, 69, 0.9);\n border-radius: 0.25rem;\n}\n\n.was-validated :invalid ~ .invalid-feedback,\n.was-validated :invalid ~ .invalid-tooltip,\n.is-invalid ~ .invalid-feedback,\n.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid {\n border-color: #dc3545;\n padding-right: calc(1.5em + 0.75rem);\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\");\n background-repeat: no-repeat;\n background-position: right calc(0.375em + 0.1875rem) center;\n background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid {\n padding-right: calc(1.5em + 0.75rem);\n background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .form-select:invalid, .form-select.is-invalid {\n border-color: #dc3545;\n}\n.was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size=\"1\"], .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid:not([multiple])[size=\"1\"] {\n padding-right: 4.125rem;\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e\"), url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\");\n background-position: right 0.75rem center, center right 2.25rem;\n background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-select:invalid:focus, .form-select.is-invalid:focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-check-input:invalid, .form-check-input.is-invalid {\n border-color: #dc3545;\n}\n.was-validated .form-check-input:invalid:checked, .form-check-input.is-invalid:checked {\n background-color: #dc3545;\n}\n.was-validated .form-check-input:invalid:focus, .form-check-input.is-invalid:focus {\n box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);\n}\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n color: #dc3545;\n}\n\n.form-check-inline .form-check-input ~ .invalid-feedback {\n margin-left: 0.5em;\n}\n\n.was-validated .input-group .form-control:invalid, .input-group .form-control.is-invalid,\n.was-validated .input-group .form-select:invalid,\n.input-group .form-select.is-invalid {\n z-index: 2;\n}\n.was-validated .input-group .form-control:invalid:focus, .input-group .form-control.is-invalid:focus,\n.was-validated .input-group .form-select:invalid:focus,\n.input-group .form-select.is-invalid:focus {\n z-index: 3;\n}\n\n.btn {\n display: inline-block;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: center;\n text-decoration: none;\n vertical-align: middle;\n cursor: pointer;\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n background-color: transparent;\n border: 1px solid transparent;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n border-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .btn {\n transition: none;\n }\n}\n.btn:hover {\n color: #212529;\n}\n.btn-check:focus + .btn, .btn:focus {\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.btn:disabled, .btn.disabled, fieldset:disabled .btn {\n pointer-events: none;\n opacity: 0.65;\n}\n\n.btn-primary {\n color: #fff;\n background-color: #0d6efd;\n border-color: #0d6efd;\n}\n.btn-primary:hover {\n color: #fff;\n background-color: #0b5ed7;\n border-color: #0a58ca;\n}\n.btn-check:focus + .btn-primary, .btn-primary:focus {\n color: #fff;\n background-color: #0b5ed7;\n border-color: #0a58ca;\n box-shadow: 0 0 0 0.25rem rgba(49, 132, 253, 0.5);\n}\n.btn-check:checked + .btn-primary, .btn-check:active + .btn-primary, .btn-primary:active, .btn-primary.active, .show > .btn-primary.dropdown-toggle {\n color: #fff;\n background-color: #0a58ca;\n border-color: #0a53be;\n}\n.btn-check:checked + .btn-primary:focus, .btn-check:active + .btn-primary:focus, .btn-primary:active:focus, .btn-primary.active:focus, .show > .btn-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.25rem rgba(49, 132, 253, 0.5);\n}\n.btn-primary:disabled, .btn-primary.disabled {\n color: #fff;\n background-color: #0d6efd;\n border-color: #0d6efd;\n}\n\n.btn-secondary {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n.btn-secondary:hover {\n color: #fff;\n background-color: #5c636a;\n border-color: #565e64;\n}\n.btn-check:focus + .btn-secondary, .btn-secondary:focus {\n color: #fff;\n background-color: #5c636a;\n border-color: #565e64;\n box-shadow: 0 0 0 0.25rem rgba(130, 138, 145, 0.5);\n}\n.btn-check:checked + .btn-secondary, .btn-check:active + .btn-secondary, .btn-secondary:active, .btn-secondary.active, .show > .btn-secondary.dropdown-toggle {\n color: #fff;\n background-color: #565e64;\n border-color: #51585e;\n}\n.btn-check:checked + .btn-secondary:focus, .btn-check:active + .btn-secondary:focus, .btn-secondary:active:focus, .btn-secondary.active:focus, .show > .btn-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.25rem rgba(130, 138, 145, 0.5);\n}\n.btn-secondary:disabled, .btn-secondary.disabled {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-success {\n color: #fff;\n background-color: #198754;\n border-color: #198754;\n}\n.btn-success:hover {\n color: #fff;\n background-color: #157347;\n border-color: #146c43;\n}\n.btn-check:focus + .btn-success, .btn-success:focus {\n color: #fff;\n background-color: #157347;\n border-color: #146c43;\n box-shadow: 0 0 0 0.25rem rgba(60, 153, 110, 0.5);\n}\n.btn-check:checked + .btn-success, .btn-check:active + .btn-success, .btn-success:active, .btn-success.active, .show > .btn-success.dropdown-toggle {\n color: #fff;\n background-color: #146c43;\n border-color: #13653f;\n}\n.btn-check:checked + .btn-success:focus, .btn-check:active + .btn-success:focus, .btn-success:active:focus, .btn-success.active:focus, .show > .btn-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.25rem rgba(60, 153, 110, 0.5);\n}\n.btn-success:disabled, .btn-success.disabled {\n color: #fff;\n background-color: #198754;\n border-color: #198754;\n}\n\n.btn-info {\n color: #000;\n background-color: #0dcaf0;\n border-color: #0dcaf0;\n}\n.btn-info:hover {\n color: #000;\n background-color: #31d2f2;\n border-color: #25cff2;\n}\n.btn-check:focus + .btn-info, .btn-info:focus {\n color: #000;\n background-color: #31d2f2;\n border-color: #25cff2;\n box-shadow: 0 0 0 0.25rem rgba(11, 172, 204, 0.5);\n}\n.btn-check:checked + .btn-info, .btn-check:active + .btn-info, .btn-info:active, .btn-info.active, .show > .btn-info.dropdown-toggle {\n color: #000;\n background-color: #3dd5f3;\n border-color: #25cff2;\n}\n.btn-check:checked + .btn-info:focus, .btn-check:active + .btn-info:focus, .btn-info:active:focus, .btn-info.active:focus, .show > .btn-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.25rem rgba(11, 172, 204, 0.5);\n}\n.btn-info:disabled, .btn-info.disabled {\n color: #000;\n background-color: #0dcaf0;\n border-color: #0dcaf0;\n}\n\n.btn-warning {\n color: #000;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n.btn-warning:hover {\n color: #000;\n background-color: #ffca2c;\n border-color: #ffc720;\n}\n.btn-check:focus + .btn-warning, .btn-warning:focus {\n color: #000;\n background-color: #ffca2c;\n border-color: #ffc720;\n box-shadow: 0 0 0 0.25rem rgba(217, 164, 6, 0.5);\n}\n.btn-check:checked + .btn-warning, .btn-check:active + .btn-warning, .btn-warning:active, .btn-warning.active, .show > .btn-warning.dropdown-toggle {\n color: #000;\n background-color: #ffcd39;\n border-color: #ffc720;\n}\n.btn-check:checked + .btn-warning:focus, .btn-check:active + .btn-warning:focus, .btn-warning:active:focus, .btn-warning.active:focus, .show > .btn-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.25rem rgba(217, 164, 6, 0.5);\n}\n.btn-warning:disabled, .btn-warning.disabled {\n color: #000;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-danger {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n.btn-danger:hover {\n color: #fff;\n background-color: #bb2d3b;\n border-color: #b02a37;\n}\n.btn-check:focus + .btn-danger, .btn-danger:focus {\n color: #fff;\n background-color: #bb2d3b;\n border-color: #b02a37;\n box-shadow: 0 0 0 0.25rem rgba(225, 83, 97, 0.5);\n}\n.btn-check:checked + .btn-danger, .btn-check:active + .btn-danger, .btn-danger:active, .btn-danger.active, .show > .btn-danger.dropdown-toggle {\n color: #fff;\n background-color: #b02a37;\n border-color: #a52834;\n}\n.btn-check:checked + .btn-danger:focus, .btn-check:active + .btn-danger:focus, .btn-danger:active:focus, .btn-danger.active:focus, .show > .btn-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.25rem rgba(225, 83, 97, 0.5);\n}\n.btn-danger:disabled, .btn-danger.disabled {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-light {\n color: #000;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n.btn-light:hover {\n color: #000;\n background-color: #f9fafb;\n border-color: #f9fafb;\n}\n.btn-check:focus + .btn-light, .btn-light:focus {\n color: #000;\n background-color: #f9fafb;\n border-color: #f9fafb;\n box-shadow: 0 0 0 0.25rem rgba(211, 212, 213, 0.5);\n}\n.btn-check:checked + .btn-light, .btn-check:active + .btn-light, .btn-light:active, .btn-light.active, .show > .btn-light.dropdown-toggle {\n color: #000;\n background-color: #f9fafb;\n border-color: #f9fafb;\n}\n.btn-check:checked + .btn-light:focus, .btn-check:active + .btn-light:focus, .btn-light:active:focus, .btn-light.active:focus, .show > .btn-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.25rem rgba(211, 212, 213, 0.5);\n}\n.btn-light:disabled, .btn-light.disabled {\n color: #000;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-dark {\n color: #fff;\n background-color: #212529;\n border-color: #212529;\n}\n.btn-dark:hover {\n color: #fff;\n background-color: #1c1f23;\n border-color: #1a1e21;\n}\n.btn-check:focus + .btn-dark, .btn-dark:focus {\n color: #fff;\n background-color: #1c1f23;\n border-color: #1a1e21;\n box-shadow: 0 0 0 0.25rem rgba(66, 70, 73, 0.5);\n}\n.btn-check:checked + .btn-dark, .btn-check:active + .btn-dark, .btn-dark:active, .btn-dark.active, .show > .btn-dark.dropdown-toggle {\n color: #fff;\n background-color: #1a1e21;\n border-color: #191c1f;\n}\n.btn-check:checked + .btn-dark:focus, .btn-check:active + .btn-dark:focus, .btn-dark:active:focus, .btn-dark.active:focus, .show > .btn-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.25rem rgba(66, 70, 73, 0.5);\n}\n.btn-dark:disabled, .btn-dark.disabled {\n color: #fff;\n background-color: #212529;\n border-color: #212529;\n}\n\n.btn-outline-primary {\n color: #0d6efd;\n border-color: #0d6efd;\n}\n.btn-outline-primary:hover {\n color: #fff;\n background-color: #0d6efd;\n border-color: #0d6efd;\n}\n.btn-check:focus + .btn-outline-primary, .btn-outline-primary:focus {\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.5);\n}\n.btn-check:checked + .btn-outline-primary, .btn-check:active + .btn-outline-primary, .btn-outline-primary:active, .btn-outline-primary.active, .btn-outline-primary.dropdown-toggle.show {\n color: #fff;\n background-color: #0d6efd;\n border-color: #0d6efd;\n}\n.btn-check:checked + .btn-outline-primary:focus, .btn-check:active + .btn-outline-primary:focus, .btn-outline-primary:active:focus, .btn-outline-primary.active:focus, .btn-outline-primary.dropdown-toggle.show:focus {\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.5);\n}\n.btn-outline-primary:disabled, .btn-outline-primary.disabled {\n color: #0d6efd;\n background-color: transparent;\n}\n\n.btn-outline-secondary {\n color: #6c757d;\n border-color: #6c757d;\n}\n.btn-outline-secondary:hover {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n.btn-check:focus + .btn-outline-secondary, .btn-outline-secondary:focus {\n box-shadow: 0 0 0 0.25rem rgba(108, 117, 125, 0.5);\n}\n.btn-check:checked + .btn-outline-secondary, .btn-check:active + .btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n.btn-check:checked + .btn-outline-secondary:focus, .btn-check:active + .btn-outline-secondary:focus, .btn-outline-secondary:active:focus, .btn-outline-secondary.active:focus, .btn-outline-secondary.dropdown-toggle.show:focus {\n box-shadow: 0 0 0 0.25rem rgba(108, 117, 125, 0.5);\n}\n.btn-outline-secondary:disabled, .btn-outline-secondary.disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.btn-outline-success {\n color: #198754;\n border-color: #198754;\n}\n.btn-outline-success:hover {\n color: #fff;\n background-color: #198754;\n border-color: #198754;\n}\n.btn-check:focus + .btn-outline-success, .btn-outline-success:focus {\n box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.5);\n}\n.btn-check:checked + .btn-outline-success, .btn-check:active + .btn-outline-success, .btn-outline-success:active, .btn-outline-success.active, .btn-outline-success.dropdown-toggle.show {\n color: #fff;\n background-color: #198754;\n border-color: #198754;\n}\n.btn-check:checked + .btn-outline-success:focus, .btn-check:active + .btn-outline-success:focus, .btn-outline-success:active:focus, .btn-outline-success.active:focus, .btn-outline-success.dropdown-toggle.show:focus {\n box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.5);\n}\n.btn-outline-success:disabled, .btn-outline-success.disabled {\n color: #198754;\n background-color: transparent;\n}\n\n.btn-outline-info {\n color: #0dcaf0;\n border-color: #0dcaf0;\n}\n.btn-outline-info:hover {\n color: #000;\n background-color: #0dcaf0;\n border-color: #0dcaf0;\n}\n.btn-check:focus + .btn-outline-info, .btn-outline-info:focus {\n box-shadow: 0 0 0 0.25rem rgba(13, 202, 240, 0.5);\n}\n.btn-check:checked + .btn-outline-info, .btn-check:active + .btn-outline-info, .btn-outline-info:active, .btn-outline-info.active, .btn-outline-info.dropdown-toggle.show {\n color: #000;\n background-color: #0dcaf0;\n border-color: #0dcaf0;\n}\n.btn-check:checked + .btn-outline-info:focus, .btn-check:active + .btn-outline-info:focus, .btn-outline-info:active:focus, .btn-outline-info.active:focus, .btn-outline-info.dropdown-toggle.show:focus {\n box-shadow: 0 0 0 0.25rem rgba(13, 202, 240, 0.5);\n}\n.btn-outline-info:disabled, .btn-outline-info.disabled {\n color: #0dcaf0;\n background-color: transparent;\n}\n\n.btn-outline-warning {\n color: #ffc107;\n border-color: #ffc107;\n}\n.btn-outline-warning:hover {\n color: #000;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n.btn-check:focus + .btn-outline-warning, .btn-outline-warning:focus {\n box-shadow: 0 0 0 0.25rem rgba(255, 193, 7, 0.5);\n}\n.btn-check:checked + .btn-outline-warning, .btn-check:active + .btn-outline-warning, .btn-outline-warning:active, .btn-outline-warning.active, .btn-outline-warning.dropdown-toggle.show {\n color: #000;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n.btn-check:checked + .btn-outline-warning:focus, .btn-check:active + .btn-outline-warning:focus, .btn-outline-warning:active:focus, .btn-outline-warning.active:focus, .btn-outline-warning.dropdown-toggle.show:focus {\n box-shadow: 0 0 0 0.25rem rgba(255, 193, 7, 0.5);\n}\n.btn-outline-warning:disabled, .btn-outline-warning.disabled {\n color: #ffc107;\n background-color: transparent;\n}\n\n.btn-outline-danger {\n color: #dc3545;\n border-color: #dc3545;\n}\n.btn-outline-danger:hover {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n.btn-check:focus + .btn-outline-danger, .btn-outline-danger:focus {\n box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.5);\n}\n.btn-check:checked + .btn-outline-danger, .btn-check:active + .btn-outline-danger, .btn-outline-danger:active, .btn-outline-danger.active, .btn-outline-danger.dropdown-toggle.show {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n.btn-check:checked + .btn-outline-danger:focus, .btn-check:active + .btn-outline-danger:focus, .btn-outline-danger:active:focus, .btn-outline-danger.active:focus, .btn-outline-danger.dropdown-toggle.show:focus {\n box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.5);\n}\n.btn-outline-danger:disabled, .btn-outline-danger.disabled {\n color: #dc3545;\n background-color: transparent;\n}\n\n.btn-outline-light {\n color: #f8f9fa;\n border-color: #f8f9fa;\n}\n.btn-outline-light:hover {\n color: #000;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n.btn-check:focus + .btn-outline-light, .btn-outline-light:focus {\n box-shadow: 0 0 0 0.25rem rgba(248, 249, 250, 0.5);\n}\n.btn-check:checked + .btn-outline-light, .btn-check:active + .btn-outline-light, .btn-outline-light:active, .btn-outline-light.active, .btn-outline-light.dropdown-toggle.show {\n color: #000;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n.btn-check:checked + .btn-outline-light:focus, .btn-check:active + .btn-outline-light:focus, .btn-outline-light:active:focus, .btn-outline-light.active:focus, .btn-outline-light.dropdown-toggle.show:focus {\n box-shadow: 0 0 0 0.25rem rgba(248, 249, 250, 0.5);\n}\n.btn-outline-light:disabled, .btn-outline-light.disabled {\n color: #f8f9fa;\n background-color: transparent;\n}\n\n.btn-outline-dark {\n color: #212529;\n border-color: #212529;\n}\n.btn-outline-dark:hover {\n color: #fff;\n background-color: #212529;\n border-color: #212529;\n}\n.btn-check:focus + .btn-outline-dark, .btn-outline-dark:focus {\n box-shadow: 0 0 0 0.25rem rgba(33, 37, 41, 0.5);\n}\n.btn-check:checked + .btn-outline-dark, .btn-check:active + .btn-outline-dark, .btn-outline-dark:active, .btn-outline-dark.active, .btn-outline-dark.dropdown-toggle.show {\n color: #fff;\n background-color: #212529;\n border-color: #212529;\n}\n.btn-check:checked + .btn-outline-dark:focus, .btn-check:active + .btn-outline-dark:focus, .btn-outline-dark:active:focus, .btn-outline-dark.active:focus, .btn-outline-dark.dropdown-toggle.show:focus {\n box-shadow: 0 0 0 0.25rem rgba(33, 37, 41, 0.5);\n}\n.btn-outline-dark:disabled, .btn-outline-dark.disabled {\n color: #212529;\n background-color: transparent;\n}\n\n.btn-link {\n font-weight: 400;\n color: #0d6efd;\n text-decoration: underline;\n}\n.btn-link:hover {\n color: #0a58ca;\n}\n.btn-link:disabled, .btn-link.disabled {\n color: #6c757d;\n}\n\n.btn-lg, .btn-group-lg > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n border-radius: 0.3rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n border-radius: 0.2rem;\n}\n\n.fade {\n transition: opacity 0.15s linear;\n}\n@media (prefers-reduced-motion: reduce) {\n .fade {\n transition: none;\n }\n}\n.fade:not(.show) {\n opacity: 0;\n}\n\n.collapse:not(.show) {\n display: none;\n}\n\n.collapsing {\n height: 0;\n overflow: hidden;\n transition: height 0.35s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n .collapsing {\n transition: none;\n }\n}\n.collapsing.collapse-horizontal {\n width: 0;\n height: auto;\n transition: width 0.35s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n .collapsing.collapse-horizontal {\n transition: none;\n }\n}\n\n.dropup,\n.dropend,\n.dropdown,\n.dropstart {\n position: relative;\n}\n\n.dropdown-toggle {\n white-space: nowrap;\n}\n.dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid;\n border-right: 0.3em solid transparent;\n border-bottom: 0;\n border-left: 0.3em solid transparent;\n}\n.dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropdown-menu {\n position: absolute;\n z-index: 1000;\n display: none;\n min-width: 10rem;\n padding: 0.5rem 0;\n margin: 0;\n font-size: 1rem;\n color: #212529;\n text-align: left;\n list-style: none;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 0.25rem;\n}\n.dropdown-menu[data-bs-popper] {\n top: 100%;\n left: 0;\n margin-top: 0.125rem;\n}\n\n.dropdown-menu-start {\n --bs-position: start;\n}\n.dropdown-menu-start[data-bs-popper] {\n right: auto;\n left: 0;\n}\n\n.dropdown-menu-end {\n --bs-position: end;\n}\n.dropdown-menu-end[data-bs-popper] {\n right: 0;\n left: auto;\n}\n\n@media (min-width: 576px) {\n .dropdown-menu-sm-start {\n --bs-position: start;\n }\n .dropdown-menu-sm-start[data-bs-popper] {\n right: auto;\n left: 0;\n }\n\n .dropdown-menu-sm-end {\n --bs-position: end;\n }\n .dropdown-menu-sm-end[data-bs-popper] {\n right: 0;\n left: auto;\n }\n}\n@media (min-width: 768px) {\n .dropdown-menu-md-start {\n --bs-position: start;\n }\n .dropdown-menu-md-start[data-bs-popper] {\n right: auto;\n left: 0;\n }\n\n .dropdown-menu-md-end {\n --bs-position: end;\n }\n .dropdown-menu-md-end[data-bs-popper] {\n right: 0;\n left: auto;\n }\n}\n@media (min-width: 992px) {\n .dropdown-menu-lg-start {\n --bs-position: start;\n }\n .dropdown-menu-lg-start[data-bs-popper] {\n right: auto;\n left: 0;\n }\n\n .dropdown-menu-lg-end {\n --bs-position: end;\n }\n .dropdown-menu-lg-end[data-bs-popper] {\n right: 0;\n left: auto;\n }\n}\n@media (min-width: 1200px) {\n .dropdown-menu-xl-start {\n --bs-position: start;\n }\n .dropdown-menu-xl-start[data-bs-popper] {\n right: auto;\n left: 0;\n }\n\n .dropdown-menu-xl-end {\n --bs-position: end;\n }\n .dropdown-menu-xl-end[data-bs-popper] {\n right: 0;\n left: auto;\n }\n}\n@media (min-width: 1400px) {\n .dropdown-menu-xxl-start {\n --bs-position: start;\n }\n .dropdown-menu-xxl-start[data-bs-popper] {\n right: auto;\n left: 0;\n }\n\n .dropdown-menu-xxl-end {\n --bs-position: end;\n }\n .dropdown-menu-xxl-end[data-bs-popper] {\n right: 0;\n left: auto;\n }\n}\n.dropup .dropdown-menu[data-bs-popper] {\n top: auto;\n bottom: 100%;\n margin-top: 0;\n margin-bottom: 0.125rem;\n}\n.dropup .dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0;\n border-right: 0.3em solid transparent;\n border-bottom: 0.3em solid;\n border-left: 0.3em solid transparent;\n}\n.dropup .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropend .dropdown-menu[data-bs-popper] {\n top: 0;\n right: auto;\n left: 100%;\n margin-top: 0;\n margin-left: 0.125rem;\n}\n.dropend .dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0;\n border-bottom: 0.3em solid transparent;\n border-left: 0.3em solid;\n}\n.dropend .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n.dropend .dropdown-toggle::after {\n vertical-align: 0;\n}\n\n.dropstart .dropdown-menu[data-bs-popper] {\n top: 0;\n right: 100%;\n left: auto;\n margin-top: 0;\n margin-right: 0.125rem;\n}\n.dropstart .dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n}\n.dropstart .dropdown-toggle::after {\n display: none;\n}\n.dropstart .dropdown-toggle::before {\n display: inline-block;\n margin-right: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0.3em solid;\n border-bottom: 0.3em solid transparent;\n}\n.dropstart .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n.dropstart .dropdown-toggle::before {\n vertical-align: 0;\n}\n\n.dropdown-divider {\n height: 0;\n margin: 0.5rem 0;\n overflow: hidden;\n border-top: 1px solid rgba(0, 0, 0, 0.15);\n}\n\n.dropdown-item {\n display: block;\n width: 100%;\n padding: 0.25rem 1rem;\n clear: both;\n font-weight: 400;\n color: #212529;\n text-align: inherit;\n text-decoration: none;\n white-space: nowrap;\n background-color: transparent;\n border: 0;\n}\n.dropdown-item:hover, .dropdown-item:focus {\n color: #1e2125;\n background-color: #e9ecef;\n}\n.dropdown-item.active, .dropdown-item:active {\n color: #fff;\n text-decoration: none;\n background-color: #0d6efd;\n}\n.dropdown-item.disabled, .dropdown-item:disabled {\n color: #adb5bd;\n pointer-events: none;\n background-color: transparent;\n}\n\n.dropdown-menu.show {\n display: block;\n}\n\n.dropdown-header {\n display: block;\n padding: 0.5rem 1rem;\n margin-bottom: 0;\n font-size: 0.875rem;\n color: #6c757d;\n white-space: nowrap;\n}\n\n.dropdown-item-text {\n display: block;\n padding: 0.25rem 1rem;\n color: #212529;\n}\n\n.dropdown-menu-dark {\n color: #dee2e6;\n background-color: #343a40;\n border-color: rgba(0, 0, 0, 0.15);\n}\n.dropdown-menu-dark .dropdown-item {\n color: #dee2e6;\n}\n.dropdown-menu-dark .dropdown-item:hover, .dropdown-menu-dark .dropdown-item:focus {\n color: #fff;\n background-color: rgba(255, 255, 255, 0.15);\n}\n.dropdown-menu-dark .dropdown-item.active, .dropdown-menu-dark .dropdown-item:active {\n color: #fff;\n background-color: #0d6efd;\n}\n.dropdown-menu-dark .dropdown-item.disabled, .dropdown-menu-dark .dropdown-item:disabled {\n color: #adb5bd;\n}\n.dropdown-menu-dark .dropdown-divider {\n border-color: rgba(0, 0, 0, 0.15);\n}\n.dropdown-menu-dark .dropdown-item-text {\n color: #dee2e6;\n}\n.dropdown-menu-dark .dropdown-header {\n color: #adb5bd;\n}\n\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-flex;\n vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n flex: 1 1 auto;\n}\n.btn-group > .btn-check:checked + .btn,\n.btn-group > .btn-check:focus + .btn,\n.btn-group > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn-check:checked + .btn,\n.btn-group-vertical > .btn-check:focus + .btn,\n.btn-group-vertical > .btn:hover,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n z-index: 1;\n}\n\n.btn-toolbar {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-start;\n}\n.btn-toolbar .input-group {\n width: auto;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) {\n margin-left: -1px;\n}\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn-group:not(:last-child) > .btn {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n.btn-group > .btn:nth-child(n+3),\n.btn-group > :not(.btn-check) + .btn,\n.btn-group > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n padding-right: 0.5625rem;\n padding-left: 0.5625rem;\n}\n.dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropend .dropdown-toggle-split::after {\n margin-left: 0;\n}\n.dropstart .dropdown-toggle-split::before {\n margin-right: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n padding-right: 0.375rem;\n padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n padding-right: 0.75rem;\n padding-left: 0.75rem;\n}\n\n.btn-group-vertical {\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group {\n width: 100%;\n}\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) {\n margin-top: -1px;\n}\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn ~ .btn,\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.nav {\n display: flex;\n flex-wrap: wrap;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.nav-link {\n display: block;\n padding: 0.5rem 1rem;\n color: #0d6efd;\n text-decoration: none;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .nav-link {\n transition: none;\n }\n}\n.nav-link:hover, .nav-link:focus {\n color: #0a58ca;\n}\n.nav-link.disabled {\n color: #6c757d;\n pointer-events: none;\n cursor: default;\n}\n\n.nav-tabs {\n border-bottom: 1px solid #dee2e6;\n}\n.nav-tabs .nav-link {\n margin-bottom: -1px;\n background: none;\n border: 1px solid transparent;\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n border-color: #e9ecef #e9ecef #dee2e6;\n isolation: isolate;\n}\n.nav-tabs .nav-link.disabled {\n color: #6c757d;\n background-color: transparent;\n border-color: transparent;\n}\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n color: #495057;\n background-color: #fff;\n border-color: #dee2e6 #dee2e6 #fff;\n}\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.nav-pills .nav-link {\n background: none;\n border: 0;\n border-radius: 0.25rem;\n}\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n color: #fff;\n background-color: #0d6efd;\n}\n\n.nav-fill > .nav-link,\n.nav-fill .nav-item {\n flex: 1 1 auto;\n text-align: center;\n}\n\n.nav-justified > .nav-link,\n.nav-justified .nav-item {\n flex-basis: 0;\n flex-grow: 1;\n text-align: center;\n}\n\n.nav-fill .nav-item .nav-link,\n.nav-justified .nav-item .nav-link {\n width: 100%;\n}\n\n.tab-content > .tab-pane {\n display: none;\n}\n.tab-content > .active {\n display: block;\n}\n\n.navbar {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n.navbar > .container,\n.navbar > .container-fluid,\n.navbar > .container-sm,\n.navbar > .container-md,\n.navbar > .container-lg,\n.navbar > .container-xl,\n.navbar > .container-xxl {\n display: flex;\n flex-wrap: inherit;\n align-items: center;\n justify-content: space-between;\n}\n.navbar-brand {\n padding-top: 0.3125rem;\n padding-bottom: 0.3125rem;\n margin-right: 1rem;\n font-size: 1.25rem;\n text-decoration: none;\n white-space: nowrap;\n}\n.navbar-nav {\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n.navbar-nav .nav-link {\n padding-right: 0;\n padding-left: 0;\n}\n.navbar-nav .dropdown-menu {\n position: static;\n}\n\n.navbar-text {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n\n.navbar-collapse {\n flex-basis: 100%;\n flex-grow: 1;\n align-items: center;\n}\n\n.navbar-toggler {\n padding: 0.25rem 0.75rem;\n font-size: 1.25rem;\n line-height: 1;\n background-color: transparent;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n transition: box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .navbar-toggler {\n transition: none;\n }\n}\n.navbar-toggler:hover {\n text-decoration: none;\n}\n.navbar-toggler:focus {\n text-decoration: none;\n outline: 0;\n box-shadow: 0 0 0 0.25rem;\n}\n\n.navbar-toggler-icon {\n display: inline-block;\n width: 1.5em;\n height: 1.5em;\n vertical-align: middle;\n background-repeat: no-repeat;\n background-position: center;\n background-size: 100%;\n}\n\n.navbar-nav-scroll {\n max-height: var(--bs-scroll-height, 75vh);\n overflow-y: auto;\n}\n\n@media (min-width: 576px) {\n .navbar-expand-sm {\n flex-wrap: nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-sm .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-sm .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-sm .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-sm .navbar-nav-scroll {\n overflow: visible;\n }\n .navbar-expand-sm .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-sm .navbar-toggler {\n display: none;\n }\n .navbar-expand-sm .offcanvas-header {\n display: none;\n }\n .navbar-expand-sm .offcanvas {\n position: inherit;\n bottom: 0;\n z-index: 1000;\n flex-grow: 1;\n visibility: visible !important;\n background-color: transparent;\n border-right: 0;\n border-left: 0;\n transition: none;\n transform: none;\n }\n .navbar-expand-sm .offcanvas-top,\n.navbar-expand-sm .offcanvas-bottom {\n height: auto;\n border-top: 0;\n border-bottom: 0;\n }\n .navbar-expand-sm .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n }\n}\n@media (min-width: 768px) {\n .navbar-expand-md {\n flex-wrap: nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-md .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-md .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-md .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-md .navbar-nav-scroll {\n overflow: visible;\n }\n .navbar-expand-md .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-md .navbar-toggler {\n display: none;\n }\n .navbar-expand-md .offcanvas-header {\n display: none;\n }\n .navbar-expand-md .offcanvas {\n position: inherit;\n bottom: 0;\n z-index: 1000;\n flex-grow: 1;\n visibility: visible !important;\n background-color: transparent;\n border-right: 0;\n border-left: 0;\n transition: none;\n transform: none;\n }\n .navbar-expand-md .offcanvas-top,\n.navbar-expand-md .offcanvas-bottom {\n height: auto;\n border-top: 0;\n border-bottom: 0;\n }\n .navbar-expand-md .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n }\n}\n@media (min-width: 992px) {\n .navbar-expand-lg {\n flex-wrap: nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-lg .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-lg .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-lg .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-lg .navbar-nav-scroll {\n overflow: visible;\n }\n .navbar-expand-lg .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-lg .navbar-toggler {\n display: none;\n }\n .navbar-expand-lg .offcanvas-header {\n display: none;\n }\n .navbar-expand-lg .offcanvas {\n position: inherit;\n bottom: 0;\n z-index: 1000;\n flex-grow: 1;\n visibility: visible !important;\n background-color: transparent;\n border-right: 0;\n border-left: 0;\n transition: none;\n transform: none;\n }\n .navbar-expand-lg .offcanvas-top,\n.navbar-expand-lg .offcanvas-bottom {\n height: auto;\n border-top: 0;\n border-bottom: 0;\n }\n .navbar-expand-lg .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n }\n}\n@media (min-width: 1200px) {\n .navbar-expand-xl {\n flex-wrap: nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-xl .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-xl .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-xl .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-xl .navbar-nav-scroll {\n overflow: visible;\n }\n .navbar-expand-xl .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-xl .navbar-toggler {\n display: none;\n }\n .navbar-expand-xl .offcanvas-header {\n display: none;\n }\n .navbar-expand-xl .offcanvas {\n position: inherit;\n bottom: 0;\n z-index: 1000;\n flex-grow: 1;\n visibility: visible !important;\n background-color: transparent;\n border-right: 0;\n border-left: 0;\n transition: none;\n transform: none;\n }\n .navbar-expand-xl .offcanvas-top,\n.navbar-expand-xl .offcanvas-bottom {\n height: auto;\n border-top: 0;\n border-bottom: 0;\n }\n .navbar-expand-xl .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n }\n}\n@media (min-width: 1400px) {\n .navbar-expand-xxl {\n flex-wrap: nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-xxl .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-xxl .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-xxl .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-xxl .navbar-nav-scroll {\n overflow: visible;\n }\n .navbar-expand-xxl .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-xxl .navbar-toggler {\n display: none;\n }\n .navbar-expand-xxl .offcanvas-header {\n display: none;\n }\n .navbar-expand-xxl .offcanvas {\n position: inherit;\n bottom: 0;\n z-index: 1000;\n flex-grow: 1;\n visibility: visible !important;\n background-color: transparent;\n border-right: 0;\n border-left: 0;\n transition: none;\n transform: none;\n }\n .navbar-expand-xxl .offcanvas-top,\n.navbar-expand-xxl .offcanvas-bottom {\n height: auto;\n border-top: 0;\n border-bottom: 0;\n }\n .navbar-expand-xxl .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n }\n}\n.navbar-expand {\n flex-wrap: nowrap;\n justify-content: flex-start;\n}\n.navbar-expand .navbar-nav {\n flex-direction: row;\n}\n.navbar-expand .navbar-nav .dropdown-menu {\n position: absolute;\n}\n.navbar-expand .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n}\n.navbar-expand .navbar-nav-scroll {\n overflow: visible;\n}\n.navbar-expand .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n}\n.navbar-expand .navbar-toggler {\n display: none;\n}\n.navbar-expand .offcanvas-header {\n display: none;\n}\n.navbar-expand .offcanvas {\n position: inherit;\n bottom: 0;\n z-index: 1000;\n flex-grow: 1;\n visibility: visible !important;\n background-color: transparent;\n border-right: 0;\n border-left: 0;\n transition: none;\n transform: none;\n}\n.navbar-expand .offcanvas-top,\n.navbar-expand .offcanvas-bottom {\n height: auto;\n border-top: 0;\n border-bottom: 0;\n}\n.navbar-expand .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n}\n\n.navbar-light .navbar-brand {\n color: rgba(0, 0, 0, 0.9);\n}\n.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n.navbar-light .navbar-nav .nav-link {\n color: rgba(0, 0, 0, 0.55);\n}\n.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {\n color: rgba(0, 0, 0, 0.7);\n}\n.navbar-light .navbar-nav .nav-link.disabled {\n color: rgba(0, 0, 0, 0.3);\n}\n.navbar-light .navbar-nav .show > .nav-link,\n.navbar-light .navbar-nav .nav-link.active {\n color: rgba(0, 0, 0, 0.9);\n}\n.navbar-light .navbar-toggler {\n color: rgba(0, 0, 0, 0.55);\n border-color: rgba(0, 0, 0, 0.1);\n}\n.navbar-light .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e\");\n}\n.navbar-light .navbar-text {\n color: rgba(0, 0, 0, 0.55);\n}\n.navbar-light .navbar-text a,\n.navbar-light .navbar-text a:hover,\n.navbar-light .navbar-text a:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-dark .navbar-brand {\n color: #fff;\n}\n.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {\n color: #fff;\n}\n.navbar-dark .navbar-nav .nav-link {\n color: rgba(255, 255, 255, 0.55);\n}\n.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {\n color: rgba(255, 255, 255, 0.75);\n}\n.navbar-dark .navbar-nav .nav-link.disabled {\n color: rgba(255, 255, 255, 0.25);\n}\n.navbar-dark .navbar-nav .show > .nav-link,\n.navbar-dark .navbar-nav .nav-link.active {\n color: #fff;\n}\n.navbar-dark .navbar-toggler {\n color: rgba(255, 255, 255, 0.55);\n border-color: rgba(255, 255, 255, 0.1);\n}\n.navbar-dark .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e\");\n}\n.navbar-dark .navbar-text {\n color: rgba(255, 255, 255, 0.55);\n}\n.navbar-dark .navbar-text a,\n.navbar-dark .navbar-text a:hover,\n.navbar-dark .navbar-text a:focus {\n color: #fff;\n}\n\n.card {\n position: relative;\n display: flex;\n flex-direction: column;\n min-width: 0;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: border-box;\n border: 1px solid rgba(0, 0, 0, 0.125);\n border-radius: 0.25rem;\n}\n.card > hr {\n margin-right: 0;\n margin-left: 0;\n}\n.card > .list-group {\n border-top: inherit;\n border-bottom: inherit;\n}\n.card > .list-group:first-child {\n border-top-width: 0;\n border-top-left-radius: calc(0.25rem - 1px);\n border-top-right-radius: calc(0.25rem - 1px);\n}\n.card > .list-group:last-child {\n border-bottom-width: 0;\n border-bottom-right-radius: calc(0.25rem - 1px);\n border-bottom-left-radius: calc(0.25rem - 1px);\n}\n.card > .card-header + .list-group,\n.card > .list-group + .card-footer {\n border-top: 0;\n}\n\n.card-body {\n flex: 1 1 auto;\n padding: 1rem 1rem;\n}\n\n.card-title {\n margin-bottom: 0.5rem;\n}\n\n.card-subtitle {\n margin-top: -0.25rem;\n margin-bottom: 0;\n}\n\n.card-text:last-child {\n margin-bottom: 0;\n}\n\n.card-link + .card-link {\n margin-left: 1rem;\n}\n\n.card-header {\n padding: 0.5rem 1rem;\n margin-bottom: 0;\n background-color: rgba(0, 0, 0, 0.03);\n border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n.card-header:first-child {\n border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;\n}\n\n.card-footer {\n padding: 0.5rem 1rem;\n background-color: rgba(0, 0, 0, 0.03);\n border-top: 1px solid rgba(0, 0, 0, 0.125);\n}\n.card-footer:last-child {\n border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);\n}\n\n.card-header-tabs {\n margin-right: -0.5rem;\n margin-bottom: -0.5rem;\n margin-left: -0.5rem;\n border-bottom: 0;\n}\n\n.card-header-pills {\n margin-right: -0.5rem;\n margin-left: -0.5rem;\n}\n\n.card-img-overlay {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n padding: 1rem;\n border-radius: calc(0.25rem - 1px);\n}\n\n.card-img,\n.card-img-top,\n.card-img-bottom {\n width: 100%;\n}\n\n.card-img,\n.card-img-top {\n border-top-left-radius: calc(0.25rem - 1px);\n border-top-right-radius: calc(0.25rem - 1px);\n}\n\n.card-img,\n.card-img-bottom {\n border-bottom-right-radius: calc(0.25rem - 1px);\n border-bottom-left-radius: calc(0.25rem - 1px);\n}\n\n.card-group > .card {\n margin-bottom: 0.75rem;\n}\n@media (min-width: 576px) {\n .card-group {\n display: flex;\n flex-flow: row wrap;\n }\n .card-group > .card {\n flex: 1 0 0%;\n margin-bottom: 0;\n }\n .card-group > .card + .card {\n margin-left: 0;\n border-left: 0;\n }\n .card-group > .card:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n .card-group > .card:not(:last-child) .card-img-top,\n.card-group > .card:not(:last-child) .card-header {\n border-top-right-radius: 0;\n }\n .card-group > .card:not(:last-child) .card-img-bottom,\n.card-group > .card:not(:last-child) .card-footer {\n border-bottom-right-radius: 0;\n }\n .card-group > .card:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n .card-group > .card:not(:first-child) .card-img-top,\n.card-group > .card:not(:first-child) .card-header {\n border-top-left-radius: 0;\n }\n .card-group > .card:not(:first-child) .card-img-bottom,\n.card-group > .card:not(:first-child) .card-footer {\n border-bottom-left-radius: 0;\n }\n}\n\n.accordion-button {\n position: relative;\n display: flex;\n align-items: center;\n width: 100%;\n padding: 1rem 1.25rem;\n font-size: 1rem;\n color: #212529;\n text-align: left;\n background-color: #fff;\n border: 0;\n border-radius: 0;\n overflow-anchor: none;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n .accordion-button {\n transition: none;\n }\n}\n.accordion-button:not(.collapsed) {\n color: #0c63e4;\n background-color: #e7f1ff;\n box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.125);\n}\n.accordion-button:not(.collapsed)::after {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e\");\n transform: rotate(-180deg);\n}\n.accordion-button::after {\n flex-shrink: 0;\n width: 1.25rem;\n height: 1.25rem;\n margin-left: auto;\n content: \"\";\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e\");\n background-repeat: no-repeat;\n background-size: 1.25rem;\n transition: transform 0.2s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .accordion-button::after {\n transition: none;\n }\n}\n.accordion-button:hover {\n z-index: 2;\n}\n.accordion-button:focus {\n z-index: 3;\n border-color: #86b7fe;\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n\n.accordion-header {\n margin-bottom: 0;\n}\n\n.accordion-item {\n background-color: #fff;\n border: 1px solid rgba(0, 0, 0, 0.125);\n}\n.accordion-item:first-of-type {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n.accordion-item:first-of-type .accordion-button {\n border-top-left-radius: calc(0.25rem - 1px);\n border-top-right-radius: calc(0.25rem - 1px);\n}\n.accordion-item:not(:first-of-type) {\n border-top: 0;\n}\n.accordion-item:last-of-type {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n.accordion-item:last-of-type .accordion-button.collapsed {\n border-bottom-right-radius: calc(0.25rem - 1px);\n border-bottom-left-radius: calc(0.25rem - 1px);\n}\n.accordion-item:last-of-type .accordion-collapse {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.accordion-body {\n padding: 1rem 1.25rem;\n}\n\n.accordion-flush .accordion-collapse {\n border-width: 0;\n}\n.accordion-flush .accordion-item {\n border-right: 0;\n border-left: 0;\n border-radius: 0;\n}\n.accordion-flush .accordion-item:first-child {\n border-top: 0;\n}\n.accordion-flush .accordion-item:last-child {\n border-bottom: 0;\n}\n.accordion-flush .accordion-item .accordion-button {\n border-radius: 0;\n}\n\n.breadcrumb {\n display: flex;\n flex-wrap: wrap;\n padding: 0 0;\n margin-bottom: 1rem;\n list-style: none;\n}\n\n.breadcrumb-item + .breadcrumb-item {\n padding-left: 0.5rem;\n}\n.breadcrumb-item + .breadcrumb-item::before {\n float: left;\n padding-right: 0.5rem;\n color: #6c757d;\n content: var(--bs-breadcrumb-divider, \"/\") /* rtl: var(--bs-breadcrumb-divider, \"/\") */;\n}\n.breadcrumb-item.active {\n color: #6c757d;\n}\n\n.pagination {\n display: flex;\n padding-left: 0;\n list-style: none;\n}\n\n.page-link {\n position: relative;\n display: block;\n color: #0d6efd;\n text-decoration: none;\n background-color: #fff;\n border: 1px solid #dee2e6;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .page-link {\n transition: none;\n }\n}\n.page-link:hover {\n z-index: 2;\n color: #0a58ca;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n.page-link:focus {\n z-index: 3;\n color: #0a58ca;\n background-color: #e9ecef;\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n\n.page-item:not(:first-child) .page-link {\n margin-left: -1px;\n}\n.page-item.active .page-link {\n z-index: 3;\n color: #fff;\n background-color: #0d6efd;\n border-color: #0d6efd;\n}\n.page-item.disabled .page-link {\n color: #6c757d;\n pointer-events: none;\n background-color: #fff;\n border-color: #dee2e6;\n}\n\n.page-link {\n padding: 0.375rem 0.75rem;\n}\n\n.page-item:first-child .page-link {\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n.page-item:last-child .page-link {\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n}\n\n.pagination-lg .page-link {\n padding: 0.75rem 1.5rem;\n font-size: 1.25rem;\n}\n.pagination-lg .page-item:first-child .page-link {\n border-top-left-radius: 0.3rem;\n border-bottom-left-radius: 0.3rem;\n}\n.pagination-lg .page-item:last-child .page-link {\n border-top-right-radius: 0.3rem;\n border-bottom-right-radius: 0.3rem;\n}\n\n.pagination-sm .page-link {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n}\n.pagination-sm .page-item:first-child .page-link {\n border-top-left-radius: 0.2rem;\n border-bottom-left-radius: 0.2rem;\n}\n.pagination-sm .page-item:last-child .page-link {\n border-top-right-radius: 0.2rem;\n border-bottom-right-radius: 0.2rem;\n}\n\n.badge {\n display: inline-block;\n padding: 0.35em 0.65em;\n font-size: 0.75em;\n font-weight: 700;\n line-height: 1;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: 0.25rem;\n}\n.badge:empty {\n display: none;\n}\n\n.btn .badge {\n position: relative;\n top: -1px;\n}\n\n.alert {\n position: relative;\n padding: 1rem 1rem;\n margin-bottom: 1rem;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.alert-heading {\n color: inherit;\n}\n\n.alert-link {\n font-weight: 700;\n}\n\n.alert-dismissible {\n padding-right: 3rem;\n}\n.alert-dismissible .btn-close {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n padding: 1.25rem 1rem;\n}\n\n.alert-primary {\n color: #084298;\n background-color: #cfe2ff;\n border-color: #b6d4fe;\n}\n.alert-primary .alert-link {\n color: #06357a;\n}\n\n.alert-secondary {\n color: #41464b;\n background-color: #e2e3e5;\n border-color: #d3d6d8;\n}\n.alert-secondary .alert-link {\n color: #34383c;\n}\n\n.alert-success {\n color: #0f5132;\n background-color: #d1e7dd;\n border-color: #badbcc;\n}\n.alert-success .alert-link {\n color: #0c4128;\n}\n\n.alert-info {\n color: #055160;\n background-color: #cff4fc;\n border-color: #b6effb;\n}\n.alert-info .alert-link {\n color: #04414d;\n}\n\n.alert-warning {\n color: #664d03;\n background-color: #fff3cd;\n border-color: #ffecb5;\n}\n.alert-warning .alert-link {\n color: #523e02;\n}\n\n.alert-danger {\n color: #842029;\n background-color: #f8d7da;\n border-color: #f5c2c7;\n}\n.alert-danger .alert-link {\n color: #6a1a21;\n}\n\n.alert-light {\n color: #636464;\n background-color: #fefefe;\n border-color: #fdfdfe;\n}\n.alert-light .alert-link {\n color: #4f5050;\n}\n\n.alert-dark {\n color: #141619;\n background-color: #d3d3d4;\n border-color: #bcbebf;\n}\n.alert-dark .alert-link {\n color: #101214;\n}\n\n@-webkit-keyframes progress-bar-stripes {\n 0% {\n background-position-x: 1rem;\n }\n}\n\n@keyframes progress-bar-stripes {\n 0% {\n background-position-x: 1rem;\n }\n}\n.progress {\n display: flex;\n height: 1rem;\n overflow: hidden;\n font-size: 0.75rem;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.progress-bar {\n display: flex;\n flex-direction: column;\n justify-content: center;\n overflow: hidden;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n background-color: #0d6efd;\n transition: width 0.6s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n .progress-bar {\n transition: none;\n }\n}\n\n.progress-bar-striped {\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 1rem 1rem;\n}\n\n.progress-bar-animated {\n -webkit-animation: 1s linear infinite progress-bar-stripes;\n animation: 1s linear infinite progress-bar-stripes;\n}\n@media (prefers-reduced-motion: reduce) {\n .progress-bar-animated {\n -webkit-animation: none;\n animation: none;\n }\n}\n\n.list-group {\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n border-radius: 0.25rem;\n}\n\n.list-group-numbered {\n list-style-type: none;\n counter-reset: section;\n}\n.list-group-numbered > li::before {\n content: counters(section, \".\") \". \";\n counter-increment: section;\n}\n\n.list-group-item-action {\n width: 100%;\n color: #495057;\n text-align: inherit;\n}\n.list-group-item-action:hover, .list-group-item-action:focus {\n z-index: 1;\n color: #495057;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n.list-group-item-action:active {\n color: #212529;\n background-color: #e9ecef;\n}\n\n.list-group-item {\n position: relative;\n display: block;\n padding: 0.5rem 1rem;\n color: #212529;\n text-decoration: none;\n background-color: #fff;\n border: 1px solid rgba(0, 0, 0, 0.125);\n}\n.list-group-item:first-child {\n border-top-left-radius: inherit;\n border-top-right-radius: inherit;\n}\n.list-group-item:last-child {\n border-bottom-right-radius: inherit;\n border-bottom-left-radius: inherit;\n}\n.list-group-item.disabled, .list-group-item:disabled {\n color: #6c757d;\n pointer-events: none;\n background-color: #fff;\n}\n.list-group-item.active {\n z-index: 2;\n color: #fff;\n background-color: #0d6efd;\n border-color: #0d6efd;\n}\n.list-group-item + .list-group-item {\n border-top-width: 0;\n}\n.list-group-item + .list-group-item.active {\n margin-top: -1px;\n border-top-width: 1px;\n}\n\n.list-group-horizontal {\n flex-direction: row;\n}\n.list-group-horizontal > .list-group-item:first-child {\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n}\n.list-group-horizontal > .list-group-item:last-child {\n border-top-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n}\n.list-group-horizontal > .list-group-item.active {\n margin-top: 0;\n}\n.list-group-horizontal > .list-group-item + .list-group-item {\n border-top-width: 1px;\n border-left-width: 0;\n}\n.list-group-horizontal > .list-group-item + .list-group-item.active {\n margin-left: -1px;\n border-left-width: 1px;\n}\n\n@media (min-width: 576px) {\n .list-group-horizontal-sm {\n flex-direction: row;\n }\n .list-group-horizontal-sm > .list-group-item:first-child {\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-sm > .list-group-item:last-child {\n border-top-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n .list-group-horizontal-sm > .list-group-item.active {\n margin-top: 0;\n }\n .list-group-horizontal-sm > .list-group-item + .list-group-item {\n border-top-width: 1px;\n border-left-width: 0;\n }\n .list-group-horizontal-sm > .list-group-item + .list-group-item.active {\n margin-left: -1px;\n border-left-width: 1px;\n }\n}\n@media (min-width: 768px) {\n .list-group-horizontal-md {\n flex-direction: row;\n }\n .list-group-horizontal-md > .list-group-item:first-child {\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-md > .list-group-item:last-child {\n border-top-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n .list-group-horizontal-md > .list-group-item.active {\n margin-top: 0;\n }\n .list-group-horizontal-md > .list-group-item + .list-group-item {\n border-top-width: 1px;\n border-left-width: 0;\n }\n .list-group-horizontal-md > .list-group-item + .list-group-item.active {\n margin-left: -1px;\n border-left-width: 1px;\n }\n}\n@media (min-width: 992px) {\n .list-group-horizontal-lg {\n flex-direction: row;\n }\n .list-group-horizontal-lg > .list-group-item:first-child {\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-lg > .list-group-item:last-child {\n border-top-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n .list-group-horizontal-lg > .list-group-item.active {\n margin-top: 0;\n }\n .list-group-horizontal-lg > .list-group-item + .list-group-item {\n border-top-width: 1px;\n border-left-width: 0;\n }\n .list-group-horizontal-lg > .list-group-item + .list-group-item.active {\n margin-left: -1px;\n border-left-width: 1px;\n }\n}\n@media (min-width: 1200px) {\n .list-group-horizontal-xl {\n flex-direction: row;\n }\n .list-group-horizontal-xl > .list-group-item:first-child {\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-xl > .list-group-item:last-child {\n border-top-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n .list-group-horizontal-xl > .list-group-item.active {\n margin-top: 0;\n }\n .list-group-horizontal-xl > .list-group-item + .list-group-item {\n border-top-width: 1px;\n border-left-width: 0;\n }\n .list-group-horizontal-xl > .list-group-item + .list-group-item.active {\n margin-left: -1px;\n border-left-width: 1px;\n }\n}\n@media (min-width: 1400px) {\n .list-group-horizontal-xxl {\n flex-direction: row;\n }\n .list-group-horizontal-xxl > .list-group-item:first-child {\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-xxl > .list-group-item:last-child {\n border-top-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n .list-group-horizontal-xxl > .list-group-item.active {\n margin-top: 0;\n }\n .list-group-horizontal-xxl > .list-group-item + .list-group-item {\n border-top-width: 1px;\n border-left-width: 0;\n }\n .list-group-horizontal-xxl > .list-group-item + .list-group-item.active {\n margin-left: -1px;\n border-left-width: 1px;\n }\n}\n.list-group-flush {\n border-radius: 0;\n}\n.list-group-flush > .list-group-item {\n border-width: 0 0 1px;\n}\n.list-group-flush > .list-group-item:last-child {\n border-bottom-width: 0;\n}\n\n.list-group-item-primary {\n color: #084298;\n background-color: #cfe2ff;\n}\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n color: #084298;\n background-color: #bacbe6;\n}\n.list-group-item-primary.list-group-item-action.active {\n color: #fff;\n background-color: #084298;\n border-color: #084298;\n}\n\n.list-group-item-secondary {\n color: #41464b;\n background-color: #e2e3e5;\n}\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n color: #41464b;\n background-color: #cbccce;\n}\n.list-group-item-secondary.list-group-item-action.active {\n color: #fff;\n background-color: #41464b;\n border-color: #41464b;\n}\n\n.list-group-item-success {\n color: #0f5132;\n background-color: #d1e7dd;\n}\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n color: #0f5132;\n background-color: #bcd0c7;\n}\n.list-group-item-success.list-group-item-action.active {\n color: #fff;\n background-color: #0f5132;\n border-color: #0f5132;\n}\n\n.list-group-item-info {\n color: #055160;\n background-color: #cff4fc;\n}\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n color: #055160;\n background-color: #badce3;\n}\n.list-group-item-info.list-group-item-action.active {\n color: #fff;\n background-color: #055160;\n border-color: #055160;\n}\n\n.list-group-item-warning {\n color: #664d03;\n background-color: #fff3cd;\n}\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n color: #664d03;\n background-color: #e6dbb9;\n}\n.list-group-item-warning.list-group-item-action.active {\n color: #fff;\n background-color: #664d03;\n border-color: #664d03;\n}\n\n.list-group-item-danger {\n color: #842029;\n background-color: #f8d7da;\n}\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n color: #842029;\n background-color: #dfc2c4;\n}\n.list-group-item-danger.list-group-item-action.active {\n color: #fff;\n background-color: #842029;\n border-color: #842029;\n}\n\n.list-group-item-light {\n color: #636464;\n background-color: #fefefe;\n}\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n color: #636464;\n background-color: #e5e5e5;\n}\n.list-group-item-light.list-group-item-action.active {\n color: #fff;\n background-color: #636464;\n border-color: #636464;\n}\n\n.list-group-item-dark {\n color: #141619;\n background-color: #d3d3d4;\n}\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n color: #141619;\n background-color: #bebebf;\n}\n.list-group-item-dark.list-group-item-action.active {\n color: #fff;\n background-color: #141619;\n border-color: #141619;\n}\n\n.btn-close {\n box-sizing: content-box;\n width: 1em;\n height: 1em;\n padding: 0.25em 0.25em;\n color: #000;\n background: transparent url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e\") center/1em auto no-repeat;\n border: 0;\n border-radius: 0.25rem;\n opacity: 0.5;\n}\n.btn-close:hover {\n color: #000;\n text-decoration: none;\n opacity: 0.75;\n}\n.btn-close:focus {\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n opacity: 1;\n}\n.btn-close:disabled, .btn-close.disabled {\n pointer-events: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n opacity: 0.25;\n}\n\n.btn-close-white {\n filter: invert(1) grayscale(100%) brightness(200%);\n}\n\n.toast {\n width: 350px;\n max-width: 100%;\n font-size: 0.875rem;\n pointer-events: auto;\n background-color: rgba(255, 255, 255, 0.85);\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.1);\n box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);\n border-radius: 0.25rem;\n}\n.toast.showing {\n opacity: 0;\n}\n.toast:not(.show) {\n display: none;\n}\n\n.toast-container {\n width: -webkit-max-content;\n width: -moz-max-content;\n width: max-content;\n max-width: 100%;\n pointer-events: none;\n}\n.toast-container > :not(:last-child) {\n margin-bottom: 0.75rem;\n}\n\n.toast-header {\n display: flex;\n align-items: center;\n padding: 0.5rem 0.75rem;\n color: #6c757d;\n background-color: rgba(255, 255, 255, 0.85);\n background-clip: padding-box;\n border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n border-top-left-radius: calc(0.25rem - 1px);\n border-top-right-radius: calc(0.25rem - 1px);\n}\n.toast-header .btn-close {\n margin-right: -0.375rem;\n margin-left: 0.75rem;\n}\n\n.toast-body {\n padding: 0.75rem;\n word-wrap: break-word;\n}\n\n.modal {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 1055;\n display: none;\n width: 100%;\n height: 100%;\n overflow-x: hidden;\n overflow-y: auto;\n outline: 0;\n}\n\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 0.5rem;\n pointer-events: none;\n}\n.modal.fade .modal-dialog {\n transition: transform 0.3s ease-out;\n transform: translate(0, -50px);\n}\n@media (prefers-reduced-motion: reduce) {\n .modal.fade .modal-dialog {\n transition: none;\n }\n}\n.modal.show .modal-dialog {\n transform: none;\n}\n.modal.modal-static .modal-dialog {\n transform: scale(1.02);\n}\n\n.modal-dialog-scrollable {\n height: calc(100% - 1rem);\n}\n.modal-dialog-scrollable .modal-content {\n max-height: 100%;\n overflow: hidden;\n}\n.modal-dialog-scrollable .modal-body {\n overflow-y: auto;\n}\n\n.modal-dialog-centered {\n display: flex;\n align-items: center;\n min-height: calc(100% - 1rem);\n}\n\n.modal-content {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n pointer-events: auto;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n outline: 0;\n}\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 1050;\n width: 100vw;\n height: 100vh;\n background-color: #000;\n}\n.modal-backdrop.fade {\n opacity: 0;\n}\n.modal-backdrop.show {\n opacity: 0.5;\n}\n\n.modal-header {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: space-between;\n padding: 1rem 1rem;\n border-bottom: 1px solid #dee2e6;\n border-top-left-radius: calc(0.3rem - 1px);\n border-top-right-radius: calc(0.3rem - 1px);\n}\n.modal-header .btn-close {\n padding: 0.5rem 0.5rem;\n margin: -0.5rem -0.5rem -0.5rem auto;\n}\n\n.modal-title {\n margin-bottom: 0;\n line-height: 1.5;\n}\n\n.modal-body {\n position: relative;\n flex: 1 1 auto;\n padding: 1rem;\n}\n\n.modal-footer {\n display: flex;\n flex-wrap: wrap;\n flex-shrink: 0;\n align-items: center;\n justify-content: flex-end;\n padding: 0.75rem;\n border-top: 1px solid #dee2e6;\n border-bottom-right-radius: calc(0.3rem - 1px);\n border-bottom-left-radius: calc(0.3rem - 1px);\n}\n.modal-footer > * {\n margin: 0.25rem;\n}\n\n@media (min-width: 576px) {\n .modal-dialog {\n max-width: 500px;\n margin: 1.75rem auto;\n }\n\n .modal-dialog-scrollable {\n height: calc(100% - 3.5rem);\n }\n\n .modal-dialog-centered {\n min-height: calc(100% - 3.5rem);\n }\n\n .modal-sm {\n max-width: 300px;\n }\n}\n@media (min-width: 992px) {\n .modal-lg,\n.modal-xl {\n max-width: 800px;\n }\n}\n@media (min-width: 1200px) {\n .modal-xl {\n max-width: 1140px;\n }\n}\n.modal-fullscreen {\n width: 100vw;\n max-width: none;\n height: 100%;\n margin: 0;\n}\n.modal-fullscreen .modal-content {\n height: 100%;\n border: 0;\n border-radius: 0;\n}\n.modal-fullscreen .modal-header {\n border-radius: 0;\n}\n.modal-fullscreen .modal-body {\n overflow-y: auto;\n}\n.modal-fullscreen .modal-footer {\n border-radius: 0;\n}\n\n@media (max-width: 575.98px) {\n .modal-fullscreen-sm-down {\n width: 100vw;\n max-width: none;\n height: 100%;\n margin: 0;\n }\n .modal-fullscreen-sm-down .modal-content {\n height: 100%;\n border: 0;\n border-radius: 0;\n }\n .modal-fullscreen-sm-down .modal-header {\n border-radius: 0;\n }\n .modal-fullscreen-sm-down .modal-body {\n overflow-y: auto;\n }\n .modal-fullscreen-sm-down .modal-footer {\n border-radius: 0;\n }\n}\n@media (max-width: 767.98px) {\n .modal-fullscreen-md-down {\n width: 100vw;\n max-width: none;\n height: 100%;\n margin: 0;\n }\n .modal-fullscreen-md-down .modal-content {\n height: 100%;\n border: 0;\n border-radius: 0;\n }\n .modal-fullscreen-md-down .modal-header {\n border-radius: 0;\n }\n .modal-fullscreen-md-down .modal-body {\n overflow-y: auto;\n }\n .modal-fullscreen-md-down .modal-footer {\n border-radius: 0;\n }\n}\n@media (max-width: 991.98px) {\n .modal-fullscreen-lg-down {\n width: 100vw;\n max-width: none;\n height: 100%;\n margin: 0;\n }\n .modal-fullscreen-lg-down .modal-content {\n height: 100%;\n border: 0;\n border-radius: 0;\n }\n .modal-fullscreen-lg-down .modal-header {\n border-radius: 0;\n }\n .modal-fullscreen-lg-down .modal-body {\n overflow-y: auto;\n }\n .modal-fullscreen-lg-down .modal-footer {\n border-radius: 0;\n }\n}\n@media (max-width: 1199.98px) {\n .modal-fullscreen-xl-down {\n width: 100vw;\n max-width: none;\n height: 100%;\n margin: 0;\n }\n .modal-fullscreen-xl-down .modal-content {\n height: 100%;\n border: 0;\n border-radius: 0;\n }\n .modal-fullscreen-xl-down .modal-header {\n border-radius: 0;\n }\n .modal-fullscreen-xl-down .modal-body {\n overflow-y: auto;\n }\n .modal-fullscreen-xl-down .modal-footer {\n border-radius: 0;\n }\n}\n@media (max-width: 1399.98px) {\n .modal-fullscreen-xxl-down {\n width: 100vw;\n max-width: none;\n height: 100%;\n margin: 0;\n }\n .modal-fullscreen-xxl-down .modal-content {\n height: 100%;\n border: 0;\n border-radius: 0;\n }\n .modal-fullscreen-xxl-down .modal-header {\n border-radius: 0;\n }\n .modal-fullscreen-xxl-down .modal-body {\n overflow-y: auto;\n }\n .modal-fullscreen-xxl-down .modal-footer {\n border-radius: 0;\n }\n}\n.tooltip {\n position: absolute;\n z-index: 1080;\n display: block;\n margin: 0;\n font-family: var(--bs-font-sans-serif);\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n opacity: 0;\n}\n.tooltip.show {\n opacity: 0.9;\n}\n.tooltip .tooltip-arrow {\n position: absolute;\n display: block;\n width: 0.8rem;\n height: 0.4rem;\n}\n.tooltip .tooltip-arrow::before {\n position: absolute;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-tooltip-top, .bs-tooltip-auto[data-popper-placement^=top] {\n padding: 0.4rem 0;\n}\n.bs-tooltip-top .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow {\n bottom: 0;\n}\n.bs-tooltip-top .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before {\n top: -1px;\n border-width: 0.4rem 0.4rem 0;\n border-top-color: #000;\n}\n\n.bs-tooltip-end, .bs-tooltip-auto[data-popper-placement^=right] {\n padding: 0 0.4rem;\n}\n.bs-tooltip-end .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow {\n left: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n.bs-tooltip-end .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before {\n right: -1px;\n border-width: 0.4rem 0.4rem 0.4rem 0;\n border-right-color: #000;\n}\n\n.bs-tooltip-bottom, .bs-tooltip-auto[data-popper-placement^=bottom] {\n padding: 0.4rem 0;\n}\n.bs-tooltip-bottom .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow {\n top: 0;\n}\n.bs-tooltip-bottom .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before {\n bottom: -1px;\n border-width: 0 0.4rem 0.4rem;\n border-bottom-color: #000;\n}\n\n.bs-tooltip-start, .bs-tooltip-auto[data-popper-placement^=left] {\n padding: 0 0.4rem;\n}\n.bs-tooltip-start .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow {\n right: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n.bs-tooltip-start .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before {\n left: -1px;\n border-width: 0.4rem 0 0.4rem 0.4rem;\n border-left-color: #000;\n}\n\n.tooltip-inner {\n max-width: 200px;\n padding: 0.25rem 0.5rem;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 0.25rem;\n}\n\n.popover {\n position: absolute;\n top: 0;\n left: 0 /* rtl:ignore */;\n z-index: 1070;\n display: block;\n max-width: 276px;\n font-family: var(--bs-font-sans-serif);\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n}\n.popover .popover-arrow {\n position: absolute;\n display: block;\n width: 1rem;\n height: 0.5rem;\n}\n.popover .popover-arrow::before, .popover .popover-arrow::after {\n position: absolute;\n display: block;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-popover-top > .popover-arrow, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow {\n bottom: calc(-0.5rem - 1px);\n}\n.bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before {\n bottom: 0;\n border-width: 0.5rem 0.5rem 0;\n border-top-color: rgba(0, 0, 0, 0.25);\n}\n.bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after {\n bottom: 1px;\n border-width: 0.5rem 0.5rem 0;\n border-top-color: #fff;\n}\n\n.bs-popover-end > .popover-arrow, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow {\n left: calc(-0.5rem - 1px);\n width: 0.5rem;\n height: 1rem;\n}\n.bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before {\n left: 0;\n border-width: 0.5rem 0.5rem 0.5rem 0;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n.bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after {\n left: 1px;\n border-width: 0.5rem 0.5rem 0.5rem 0;\n border-right-color: #fff;\n}\n\n.bs-popover-bottom > .popover-arrow, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow {\n top: calc(-0.5rem - 1px);\n}\n.bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before {\n top: 0;\n border-width: 0 0.5rem 0.5rem 0.5rem;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n}\n.bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after {\n top: 1px;\n border-width: 0 0.5rem 0.5rem 0.5rem;\n border-bottom-color: #fff;\n}\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[data-popper-placement^=bottom] .popover-header::before {\n position: absolute;\n top: 0;\n left: 50%;\n display: block;\n width: 1rem;\n margin-left: -0.5rem;\n content: \"\";\n border-bottom: 1px solid #f0f0f0;\n}\n\n.bs-popover-start > .popover-arrow, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow {\n right: calc(-0.5rem - 1px);\n width: 0.5rem;\n height: 1rem;\n}\n.bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before {\n right: 0;\n border-width: 0.5rem 0 0.5rem 0.5rem;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n.bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after {\n right: 1px;\n border-width: 0.5rem 0 0.5rem 0.5rem;\n border-left-color: #fff;\n}\n\n.popover-header {\n padding: 0.5rem 1rem;\n margin-bottom: 0;\n font-size: 1rem;\n background-color: #f0f0f0;\n border-bottom: 1px solid rgba(0, 0, 0, 0.2);\n border-top-left-radius: calc(0.3rem - 1px);\n border-top-right-radius: calc(0.3rem - 1px);\n}\n.popover-header:empty {\n display: none;\n}\n\n.popover-body {\n padding: 1rem 1rem;\n color: #212529;\n}\n\n.carousel {\n position: relative;\n}\n\n.carousel.pointer-event {\n touch-action: pan-y;\n}\n\n.carousel-inner {\n position: relative;\n width: 100%;\n overflow: hidden;\n}\n.carousel-inner::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.carousel-item {\n position: relative;\n display: none;\n float: left;\n width: 100%;\n margin-right: -100%;\n -webkit-backface-visibility: hidden;\n backface-visibility: hidden;\n transition: transform 0.6s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .carousel-item {\n transition: none;\n }\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n display: block;\n}\n\n/* rtl:begin:ignore */\n.carousel-item-next:not(.carousel-item-start),\n.active.carousel-item-end {\n transform: translateX(100%);\n}\n\n.carousel-item-prev:not(.carousel-item-end),\n.active.carousel-item-start {\n transform: translateX(-100%);\n}\n\n/* rtl:end:ignore */\n.carousel-fade .carousel-item {\n opacity: 0;\n transition-property: opacity;\n transform: none;\n}\n.carousel-fade .carousel-item.active,\n.carousel-fade .carousel-item-next.carousel-item-start,\n.carousel-fade .carousel-item-prev.carousel-item-end {\n z-index: 1;\n opacity: 1;\n}\n.carousel-fade .active.carousel-item-start,\n.carousel-fade .active.carousel-item-end {\n z-index: 0;\n opacity: 0;\n transition: opacity 0s 0.6s;\n}\n@media (prefers-reduced-motion: reduce) {\n .carousel-fade .active.carousel-item-start,\n.carousel-fade .active.carousel-item-end {\n transition: none;\n }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n position: absolute;\n top: 0;\n bottom: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 15%;\n padding: 0;\n color: #fff;\n text-align: center;\n background: none;\n border: 0;\n opacity: 0.5;\n transition: opacity 0.15s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n .carousel-control-prev,\n.carousel-control-next {\n transition: none;\n }\n}\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n opacity: 0.9;\n}\n\n.carousel-control-prev {\n left: 0;\n}\n\n.carousel-control-next {\n right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n display: inline-block;\n width: 2rem;\n height: 2rem;\n background-repeat: no-repeat;\n background-position: 50%;\n background-size: 100% 100%;\n}\n\n/* rtl:options: {\n \"autoRename\": true,\n \"stringMap\":[ {\n \"name\" : \"prev-next\",\n \"search\" : \"prev\",\n \"replace\" : \"next\"\n } ]\n} */\n.carousel-control-prev-icon {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e\");\n}\n\n.carousel-control-next-icon {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e\");\n}\n\n.carousel-indicators {\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 2;\n display: flex;\n justify-content: center;\n padding: 0;\n margin-right: 15%;\n margin-bottom: 1rem;\n margin-left: 15%;\n list-style: none;\n}\n.carousel-indicators [data-bs-target] {\n box-sizing: content-box;\n flex: 0 1 auto;\n width: 30px;\n height: 3px;\n padding: 0;\n margin-right: 3px;\n margin-left: 3px;\n text-indent: -999px;\n cursor: pointer;\n background-color: #fff;\n background-clip: padding-box;\n border: 0;\n border-top: 10px solid transparent;\n border-bottom: 10px solid transparent;\n opacity: 0.5;\n transition: opacity 0.6s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n .carousel-indicators [data-bs-target] {\n transition: none;\n }\n}\n.carousel-indicators .active {\n opacity: 1;\n}\n\n.carousel-caption {\n position: absolute;\n right: 15%;\n bottom: 1.25rem;\n left: 15%;\n padding-top: 1.25rem;\n padding-bottom: 1.25rem;\n color: #fff;\n text-align: center;\n}\n\n.carousel-dark .carousel-control-prev-icon,\n.carousel-dark .carousel-control-next-icon {\n filter: invert(1) grayscale(100);\n}\n.carousel-dark .carousel-indicators [data-bs-target] {\n background-color: #000;\n}\n.carousel-dark .carousel-caption {\n color: #000;\n}\n\n@-webkit-keyframes spinner-border {\n to {\n transform: rotate(360deg) /* rtl:ignore */;\n }\n}\n\n@keyframes spinner-border {\n to {\n transform: rotate(360deg) /* rtl:ignore */;\n }\n}\n.spinner-border {\n display: inline-block;\n width: 2rem;\n height: 2rem;\n vertical-align: -0.125em;\n border: 0.25em solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n -webkit-animation: 0.75s linear infinite spinner-border;\n animation: 0.75s linear infinite spinner-border;\n}\n\n.spinner-border-sm {\n width: 1rem;\n height: 1rem;\n border-width: 0.2em;\n}\n\n@-webkit-keyframes spinner-grow {\n 0% {\n transform: scale(0);\n }\n 50% {\n opacity: 1;\n transform: none;\n }\n}\n\n@keyframes spinner-grow {\n 0% {\n transform: scale(0);\n }\n 50% {\n opacity: 1;\n transform: none;\n }\n}\n.spinner-grow {\n display: inline-block;\n width: 2rem;\n height: 2rem;\n vertical-align: -0.125em;\n background-color: currentColor;\n border-radius: 50%;\n opacity: 0;\n -webkit-animation: 0.75s linear infinite spinner-grow;\n animation: 0.75s linear infinite spinner-grow;\n}\n\n.spinner-grow-sm {\n width: 1rem;\n height: 1rem;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .spinner-border,\n.spinner-grow {\n -webkit-animation-duration: 1.5s;\n animation-duration: 1.5s;\n }\n}\n.offcanvas {\n position: fixed;\n bottom: 0;\n z-index: 1045;\n display: flex;\n flex-direction: column;\n max-width: 100%;\n visibility: hidden;\n background-color: #fff;\n background-clip: padding-box;\n outline: 0;\n transition: transform 0.3s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .offcanvas {\n transition: none;\n }\n}\n\n.offcanvas-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 1040;\n width: 100vw;\n height: 100vh;\n background-color: #000;\n}\n.offcanvas-backdrop.fade {\n opacity: 0;\n}\n.offcanvas-backdrop.show {\n opacity: 0.5;\n}\n\n.offcanvas-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 1rem 1rem;\n}\n.offcanvas-header .btn-close {\n padding: 0.5rem 0.5rem;\n margin-top: -0.5rem;\n margin-right: -0.5rem;\n margin-bottom: -0.5rem;\n}\n\n.offcanvas-title {\n margin-bottom: 0;\n line-height: 1.5;\n}\n\n.offcanvas-body {\n flex-grow: 1;\n padding: 1rem 1rem;\n overflow-y: auto;\n}\n\n.offcanvas-start {\n top: 0;\n left: 0;\n width: 400px;\n border-right: 1px solid rgba(0, 0, 0, 0.2);\n transform: translateX(-100%);\n}\n\n.offcanvas-end {\n top: 0;\n right: 0;\n width: 400px;\n border-left: 1px solid rgba(0, 0, 0, 0.2);\n transform: translateX(100%);\n}\n\n.offcanvas-top {\n top: 0;\n right: 0;\n left: 0;\n height: 30vh;\n max-height: 100%;\n border-bottom: 1px solid rgba(0, 0, 0, 0.2);\n transform: translateY(-100%);\n}\n\n.offcanvas-bottom {\n right: 0;\n left: 0;\n height: 30vh;\n max-height: 100%;\n border-top: 1px solid rgba(0, 0, 0, 0.2);\n transform: translateY(100%);\n}\n\n.offcanvas.show {\n transform: none;\n}\n\n.placeholder {\n display: inline-block;\n min-height: 1em;\n vertical-align: middle;\n cursor: wait;\n background-color: currentColor;\n opacity: 0.5;\n}\n.placeholder.btn::before {\n display: inline-block;\n content: \"\";\n}\n\n.placeholder-xs {\n min-height: 0.6em;\n}\n\n.placeholder-sm {\n min-height: 0.8em;\n}\n\n.placeholder-lg {\n min-height: 1.2em;\n}\n\n.placeholder-glow .placeholder {\n -webkit-animation: placeholder-glow 2s ease-in-out infinite;\n animation: placeholder-glow 2s ease-in-out infinite;\n}\n\n@-webkit-keyframes placeholder-glow {\n 50% {\n opacity: 0.2;\n }\n}\n\n@keyframes placeholder-glow {\n 50% {\n opacity: 0.2;\n }\n}\n.placeholder-wave {\n -webkit-mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%);\n mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%);\n -webkit-mask-size: 200% 100%;\n mask-size: 200% 100%;\n -webkit-animation: placeholder-wave 2s linear infinite;\n animation: placeholder-wave 2s linear infinite;\n}\n\n@-webkit-keyframes placeholder-wave {\n 100% {\n -webkit-mask-position: -200% 0%;\n mask-position: -200% 0%;\n }\n}\n\n@keyframes placeholder-wave {\n 100% {\n -webkit-mask-position: -200% 0%;\n mask-position: -200% 0%;\n }\n}\n.clearfix::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.link-primary {\n color: #0d6efd;\n}\n.link-primary:hover, .link-primary:focus {\n color: #0a58ca;\n}\n\n.link-secondary {\n color: #6c757d;\n}\n.link-secondary:hover, .link-secondary:focus {\n color: #565e64;\n}\n\n.link-success {\n color: #198754;\n}\n.link-success:hover, .link-success:focus {\n color: #146c43;\n}\n\n.link-info {\n color: #0dcaf0;\n}\n.link-info:hover, .link-info:focus {\n color: #3dd5f3;\n}\n\n.link-warning {\n color: #ffc107;\n}\n.link-warning:hover, .link-warning:focus {\n color: #ffcd39;\n}\n\n.link-danger {\n color: #dc3545;\n}\n.link-danger:hover, .link-danger:focus {\n color: #b02a37;\n}\n\n.link-light {\n color: #f8f9fa;\n}\n.link-light:hover, .link-light:focus {\n color: #f9fafb;\n}\n\n.link-dark {\n color: #212529;\n}\n.link-dark:hover, .link-dark:focus {\n color: #1a1e21;\n}\n\n.ratio {\n position: relative;\n width: 100%;\n}\n.ratio::before {\n display: block;\n padding-top: var(--bs-aspect-ratio);\n content: \"\";\n}\n.ratio > * {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n}\n\n.ratio-1x1 {\n --bs-aspect-ratio: 100%;\n}\n\n.ratio-4x3 {\n --bs-aspect-ratio: 75%;\n}\n\n.ratio-16x9 {\n --bs-aspect-ratio: 56.25%;\n}\n\n.ratio-21x9 {\n --bs-aspect-ratio: 42.8571428571%;\n}\n\n.fixed-top {\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n\n.fixed-bottom {\n position: fixed;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1030;\n}\n\n.sticky-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n}\n\n@media (min-width: 576px) {\n .sticky-sm-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n}\n@media (min-width: 768px) {\n .sticky-md-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n}\n@media (min-width: 992px) {\n .sticky-lg-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n}\n@media (min-width: 1200px) {\n .sticky-xl-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n}\n@media (min-width: 1400px) {\n .sticky-xxl-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n}\n.hstack {\n display: flex;\n flex-direction: row;\n align-items: center;\n align-self: stretch;\n}\n\n.vstack {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n align-self: stretch;\n}\n\n.visually-hidden,\n.visually-hidden-focusable:not(:focus):not(:focus-within) {\n position: absolute !important;\n width: 1px !important;\n height: 1px !important;\n padding: 0 !important;\n margin: -1px !important;\n overflow: hidden !important;\n clip: rect(0, 0, 0, 0) !important;\n white-space: nowrap !important;\n border: 0 !important;\n}\n\n.stretched-link::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1;\n content: \"\";\n}\n\n.text-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.vr {\n display: inline-block;\n align-self: stretch;\n width: 1px;\n min-height: 1em;\n background-color: currentColor;\n opacity: 0.25;\n}\n\n.align-baseline {\n vertical-align: baseline !important;\n}\n\n.align-top {\n vertical-align: top !important;\n}\n\n.align-middle {\n vertical-align: middle !important;\n}\n\n.align-bottom {\n vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n vertical-align: text-top !important;\n}\n\n.float-start {\n float: left !important;\n}\n\n.float-end {\n float: right !important;\n}\n\n.float-none {\n float: none !important;\n}\n\n.opacity-0 {\n opacity: 0 !important;\n}\n\n.opacity-25 {\n opacity: 0.25 !important;\n}\n\n.opacity-50 {\n opacity: 0.5 !important;\n}\n\n.opacity-75 {\n opacity: 0.75 !important;\n}\n\n.opacity-100 {\n opacity: 1 !important;\n}\n\n.overflow-auto {\n overflow: auto !important;\n}\n\n.overflow-hidden {\n overflow: hidden !important;\n}\n\n.overflow-visible {\n overflow: visible !important;\n}\n\n.overflow-scroll {\n overflow: scroll !important;\n}\n\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-grid {\n display: grid !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: flex !important;\n}\n\n.d-inline-flex {\n display: inline-flex !important;\n}\n\n.d-none {\n display: none !important;\n}\n\n.shadow {\n box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-sm {\n box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow-lg {\n box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n box-shadow: none !important;\n}\n\n.position-static {\n position: static !important;\n}\n\n.position-relative {\n position: relative !important;\n}\n\n.position-absolute {\n position: absolute !important;\n}\n\n.position-fixed {\n position: fixed !important;\n}\n\n.position-sticky {\n position: -webkit-sticky !important;\n position: sticky !important;\n}\n\n.top-0 {\n top: 0 !important;\n}\n\n.top-50 {\n top: 50% !important;\n}\n\n.top-100 {\n top: 100% !important;\n}\n\n.bottom-0 {\n bottom: 0 !important;\n}\n\n.bottom-50 {\n bottom: 50% !important;\n}\n\n.bottom-100 {\n bottom: 100% !important;\n}\n\n.start-0 {\n left: 0 !important;\n}\n\n.start-50 {\n left: 50% !important;\n}\n\n.start-100 {\n left: 100% !important;\n}\n\n.end-0 {\n right: 0 !important;\n}\n\n.end-50 {\n right: 50% !important;\n}\n\n.end-100 {\n right: 100% !important;\n}\n\n.translate-middle {\n transform: translate(-50%, -50%) !important;\n}\n\n.translate-middle-x {\n transform: translateX(-50%) !important;\n}\n\n.translate-middle-y {\n transform: translateY(-50%) !important;\n}\n\n.border {\n border: 1px solid #dee2e6 !important;\n}\n\n.border-0 {\n border: 0 !important;\n}\n\n.border-top {\n border-top: 1px solid #dee2e6 !important;\n}\n\n.border-top-0 {\n border-top: 0 !important;\n}\n\n.border-end {\n border-right: 1px solid #dee2e6 !important;\n}\n\n.border-end-0 {\n border-right: 0 !important;\n}\n\n.border-bottom {\n border-bottom: 1px solid #dee2e6 !important;\n}\n\n.border-bottom-0 {\n border-bottom: 0 !important;\n}\n\n.border-start {\n border-left: 1px solid #dee2e6 !important;\n}\n\n.border-start-0 {\n border-left: 0 !important;\n}\n\n.border-primary {\n border-color: #0d6efd !important;\n}\n\n.border-secondary {\n border-color: #6c757d !important;\n}\n\n.border-success {\n border-color: #198754 !important;\n}\n\n.border-info {\n border-color: #0dcaf0 !important;\n}\n\n.border-warning {\n border-color: #ffc107 !important;\n}\n\n.border-danger {\n border-color: #dc3545 !important;\n}\n\n.border-light {\n border-color: #f8f9fa !important;\n}\n\n.border-dark {\n border-color: #212529 !important;\n}\n\n.border-white {\n border-color: #fff !important;\n}\n\n.border-1 {\n border-width: 1px !important;\n}\n\n.border-2 {\n border-width: 2px !important;\n}\n\n.border-3 {\n border-width: 3px !important;\n}\n\n.border-4 {\n border-width: 4px !important;\n}\n\n.border-5 {\n border-width: 5px !important;\n}\n\n.w-25 {\n width: 25% !important;\n}\n\n.w-50 {\n width: 50% !important;\n}\n\n.w-75 {\n width: 75% !important;\n}\n\n.w-100 {\n width: 100% !important;\n}\n\n.w-auto {\n width: auto !important;\n}\n\n.mw-100 {\n max-width: 100% !important;\n}\n\n.vw-100 {\n width: 100vw !important;\n}\n\n.min-vw-100 {\n min-width: 100vw !important;\n}\n\n.h-25 {\n height: 25% !important;\n}\n\n.h-50 {\n height: 50% !important;\n}\n\n.h-75 {\n height: 75% !important;\n}\n\n.h-100 {\n height: 100% !important;\n}\n\n.h-auto {\n height: auto !important;\n}\n\n.mh-100 {\n max-height: 100% !important;\n}\n\n.vh-100 {\n height: 100vh !important;\n}\n\n.min-vh-100 {\n min-height: 100vh !important;\n}\n\n.flex-fill {\n flex: 1 1 auto !important;\n}\n\n.flex-row {\n flex-direction: row !important;\n}\n\n.flex-column {\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n flex-direction: column-reverse !important;\n}\n\n.flex-grow-0 {\n flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n flex-shrink: 1 !important;\n}\n\n.flex-wrap {\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n}\n\n.gap-0 {\n gap: 0 !important;\n}\n\n.gap-1 {\n gap: 0.25rem !important;\n}\n\n.gap-2 {\n gap: 0.5rem !important;\n}\n\n.gap-3 {\n gap: 1rem !important;\n}\n\n.gap-4 {\n gap: 1.5rem !important;\n}\n\n.gap-5 {\n gap: 3rem !important;\n}\n\n.justify-content-start {\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n justify-content: center !important;\n}\n\n.justify-content-between {\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n justify-content: space-around !important;\n}\n\n.justify-content-evenly {\n justify-content: space-evenly !important;\n}\n\n.align-items-start {\n align-items: flex-start !important;\n}\n\n.align-items-end {\n align-items: flex-end !important;\n}\n\n.align-items-center {\n align-items: center !important;\n}\n\n.align-items-baseline {\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n align-items: stretch !important;\n}\n\n.align-content-start {\n align-content: flex-start !important;\n}\n\n.align-content-end {\n align-content: flex-end !important;\n}\n\n.align-content-center {\n align-content: center !important;\n}\n\n.align-content-between {\n align-content: space-between !important;\n}\n\n.align-content-around {\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n align-content: stretch !important;\n}\n\n.align-self-auto {\n align-self: auto !important;\n}\n\n.align-self-start {\n align-self: flex-start !important;\n}\n\n.align-self-end {\n align-self: flex-end !important;\n}\n\n.align-self-center {\n align-self: center !important;\n}\n\n.align-self-baseline {\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n align-self: stretch !important;\n}\n\n.order-first {\n order: -1 !important;\n}\n\n.order-0 {\n order: 0 !important;\n}\n\n.order-1 {\n order: 1 !important;\n}\n\n.order-2 {\n order: 2 !important;\n}\n\n.order-3 {\n order: 3 !important;\n}\n\n.order-4 {\n order: 4 !important;\n}\n\n.order-5 {\n order: 5 !important;\n}\n\n.order-last {\n order: 6 !important;\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mx-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n}\n\n.mx-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n}\n\n.mx-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n}\n\n.mx-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n}\n\n.mx-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n}\n\n.mx-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n}\n\n.mx-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n}\n\n.my-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n.my-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n}\n\n.my-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n}\n\n.my-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n}\n\n.my-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n}\n\n.my-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n}\n\n.my-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n}\n\n.mt-0 {\n margin-top: 0 !important;\n}\n\n.mt-1 {\n margin-top: 0.25rem !important;\n}\n\n.mt-2 {\n margin-top: 0.5rem !important;\n}\n\n.mt-3 {\n margin-top: 1rem !important;\n}\n\n.mt-4 {\n margin-top: 1.5rem !important;\n}\n\n.mt-5 {\n margin-top: 3rem !important;\n}\n\n.mt-auto {\n margin-top: auto !important;\n}\n\n.me-0 {\n margin-right: 0 !important;\n}\n\n.me-1 {\n margin-right: 0.25rem !important;\n}\n\n.me-2 {\n margin-right: 0.5rem !important;\n}\n\n.me-3 {\n margin-right: 1rem !important;\n}\n\n.me-4 {\n margin-right: 1.5rem !important;\n}\n\n.me-5 {\n margin-right: 3rem !important;\n}\n\n.me-auto {\n margin-right: auto !important;\n}\n\n.mb-0 {\n margin-bottom: 0 !important;\n}\n\n.mb-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.mb-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.mb-3 {\n margin-bottom: 1rem !important;\n}\n\n.mb-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.mb-5 {\n margin-bottom: 3rem !important;\n}\n\n.mb-auto {\n margin-bottom: auto !important;\n}\n\n.ms-0 {\n margin-left: 0 !important;\n}\n\n.ms-1 {\n margin-left: 0.25rem !important;\n}\n\n.ms-2 {\n margin-left: 0.5rem !important;\n}\n\n.ms-3 {\n margin-left: 1rem !important;\n}\n\n.ms-4 {\n margin-left: 1.5rem !important;\n}\n\n.ms-5 {\n margin-left: 3rem !important;\n}\n\n.ms-auto {\n margin-left: auto !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.px-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n}\n\n.px-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n}\n\n.px-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n}\n\n.px-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n}\n\n.px-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n}\n\n.px-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n}\n\n.py-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n}\n\n.py-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n}\n\n.py-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n}\n\n.py-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n}\n\n.py-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n}\n\n.py-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n}\n\n.pt-0 {\n padding-top: 0 !important;\n}\n\n.pt-1 {\n padding-top: 0.25rem !important;\n}\n\n.pt-2 {\n padding-top: 0.5rem !important;\n}\n\n.pt-3 {\n padding-top: 1rem !important;\n}\n\n.pt-4 {\n padding-top: 1.5rem !important;\n}\n\n.pt-5 {\n padding-top: 3rem !important;\n}\n\n.pe-0 {\n padding-right: 0 !important;\n}\n\n.pe-1 {\n padding-right: 0.25rem !important;\n}\n\n.pe-2 {\n padding-right: 0.5rem !important;\n}\n\n.pe-3 {\n padding-right: 1rem !important;\n}\n\n.pe-4 {\n padding-right: 1.5rem !important;\n}\n\n.pe-5 {\n padding-right: 3rem !important;\n}\n\n.pb-0 {\n padding-bottom: 0 !important;\n}\n\n.pb-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pb-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pb-3 {\n padding-bottom: 1rem !important;\n}\n\n.pb-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pb-5 {\n padding-bottom: 3rem !important;\n}\n\n.ps-0 {\n padding-left: 0 !important;\n}\n\n.ps-1 {\n padding-left: 0.25rem !important;\n}\n\n.ps-2 {\n padding-left: 0.5rem !important;\n}\n\n.ps-3 {\n padding-left: 1rem !important;\n}\n\n.ps-4 {\n padding-left: 1.5rem !important;\n}\n\n.ps-5 {\n padding-left: 3rem !important;\n}\n\n.font-monospace {\n font-family: var(--bs-font-monospace) !important;\n}\n\n.fs-1 {\n font-size: calc(1.375rem + 1.5vw) !important;\n}\n\n.fs-2 {\n font-size: calc(1.325rem + 0.9vw) !important;\n}\n\n.fs-3 {\n font-size: calc(1.3rem + 0.6vw) !important;\n}\n\n.fs-4 {\n font-size: calc(1.275rem + 0.3vw) !important;\n}\n\n.fs-5 {\n font-size: 1.25rem !important;\n}\n\n.fs-6 {\n font-size: 1rem !important;\n}\n\n.fst-italic {\n font-style: italic !important;\n}\n\n.fst-normal {\n font-style: normal !important;\n}\n\n.fw-light {\n font-weight: 300 !important;\n}\n\n.fw-lighter {\n font-weight: lighter !important;\n}\n\n.fw-normal {\n font-weight: 400 !important;\n}\n\n.fw-bold {\n font-weight: 700 !important;\n}\n\n.fw-bolder {\n font-weight: bolder !important;\n}\n\n.lh-1 {\n line-height: 1 !important;\n}\n\n.lh-sm {\n line-height: 1.25 !important;\n}\n\n.lh-base {\n line-height: 1.5 !important;\n}\n\n.lh-lg {\n line-height: 2 !important;\n}\n\n.text-start {\n text-align: left !important;\n}\n\n.text-end {\n text-align: right !important;\n}\n\n.text-center {\n text-align: center !important;\n}\n\n.text-decoration-none {\n text-decoration: none !important;\n}\n\n.text-decoration-underline {\n text-decoration: underline !important;\n}\n\n.text-decoration-line-through {\n text-decoration: line-through !important;\n}\n\n.text-lowercase {\n text-transform: lowercase !important;\n}\n\n.text-uppercase {\n text-transform: uppercase !important;\n}\n\n.text-capitalize {\n text-transform: capitalize !important;\n}\n\n.text-wrap {\n white-space: normal !important;\n}\n\n.text-nowrap {\n white-space: nowrap !important;\n}\n\n/* rtl:begin:remove */\n.text-break {\n word-wrap: break-word !important;\n word-break: break-word !important;\n}\n\n/* rtl:end:remove */\n.text-primary {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-secondary {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-success {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-info {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-warning {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-danger {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-light {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-dark {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-black {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-white {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-body {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-muted {\n --bs-text-opacity: 1;\n color: #6c757d !important;\n}\n\n.text-black-50 {\n --bs-text-opacity: 1;\n color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n --bs-text-opacity: 1;\n color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-reset {\n --bs-text-opacity: 1;\n color: inherit !important;\n}\n\n.text-opacity-25 {\n --bs-text-opacity: 0.25;\n}\n\n.text-opacity-50 {\n --bs-text-opacity: 0.5;\n}\n\n.text-opacity-75 {\n --bs-text-opacity: 0.75;\n}\n\n.text-opacity-100 {\n --bs-text-opacity: 1;\n}\n\n.bg-primary {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-secondary {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-success {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-info {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-warning {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-danger {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-light {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-dark {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-black {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-white {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-body {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-transparent {\n --bs-bg-opacity: 1;\n background-color: transparent !important;\n}\n\n.bg-opacity-10 {\n --bs-bg-opacity: 0.1;\n}\n\n.bg-opacity-25 {\n --bs-bg-opacity: 0.25;\n}\n\n.bg-opacity-50 {\n --bs-bg-opacity: 0.5;\n}\n\n.bg-opacity-75 {\n --bs-bg-opacity: 0.75;\n}\n\n.bg-opacity-100 {\n --bs-bg-opacity: 1;\n}\n\n.bg-gradient {\n background-image: var(--bs-gradient) !important;\n}\n\n.user-select-all {\n -webkit-user-select: all !important;\n -moz-user-select: all !important;\n user-select: all !important;\n}\n\n.user-select-auto {\n -webkit-user-select: auto !important;\n -moz-user-select: auto !important;\n user-select: auto !important;\n}\n\n.user-select-none {\n -webkit-user-select: none !important;\n -moz-user-select: none !important;\n user-select: none !important;\n}\n\n.pe-none {\n pointer-events: none !important;\n}\n\n.pe-auto {\n pointer-events: auto !important;\n}\n\n.rounded {\n border-radius: 0.25rem !important;\n}\n\n.rounded-0 {\n border-radius: 0 !important;\n}\n\n.rounded-1 {\n border-radius: 0.2rem !important;\n}\n\n.rounded-2 {\n border-radius: 0.25rem !important;\n}\n\n.rounded-3 {\n border-radius: 0.3rem !important;\n}\n\n.rounded-circle {\n border-radius: 50% !important;\n}\n\n.rounded-pill {\n border-radius: 50rem !important;\n}\n\n.rounded-top {\n border-top-left-radius: 0.25rem !important;\n border-top-right-radius: 0.25rem !important;\n}\n\n.rounded-end {\n border-top-right-radius: 0.25rem !important;\n border-bottom-right-radius: 0.25rem !important;\n}\n\n.rounded-bottom {\n border-bottom-right-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-start {\n border-bottom-left-radius: 0.25rem !important;\n border-top-left-radius: 0.25rem !important;\n}\n\n.visible {\n visibility: visible !important;\n}\n\n.invisible {\n visibility: hidden !important;\n}\n\n@media (min-width: 576px) {\n .float-sm-start {\n float: left !important;\n }\n\n .float-sm-end {\n float: right !important;\n }\n\n .float-sm-none {\n float: none !important;\n }\n\n .d-sm-inline {\n display: inline !important;\n }\n\n .d-sm-inline-block {\n display: inline-block !important;\n }\n\n .d-sm-block {\n display: block !important;\n }\n\n .d-sm-grid {\n display: grid !important;\n }\n\n .d-sm-table {\n display: table !important;\n }\n\n .d-sm-table-row {\n display: table-row !important;\n }\n\n .d-sm-table-cell {\n display: table-cell !important;\n }\n\n .d-sm-flex {\n display: flex !important;\n }\n\n .d-sm-inline-flex {\n display: inline-flex !important;\n }\n\n .d-sm-none {\n display: none !important;\n }\n\n .flex-sm-fill {\n flex: 1 1 auto !important;\n }\n\n .flex-sm-row {\n flex-direction: row !important;\n }\n\n .flex-sm-column {\n flex-direction: column !important;\n }\n\n .flex-sm-row-reverse {\n flex-direction: row-reverse !important;\n }\n\n .flex-sm-column-reverse {\n flex-direction: column-reverse !important;\n }\n\n .flex-sm-grow-0 {\n flex-grow: 0 !important;\n }\n\n .flex-sm-grow-1 {\n flex-grow: 1 !important;\n }\n\n .flex-sm-shrink-0 {\n flex-shrink: 0 !important;\n }\n\n .flex-sm-shrink-1 {\n flex-shrink: 1 !important;\n }\n\n .flex-sm-wrap {\n flex-wrap: wrap !important;\n }\n\n .flex-sm-nowrap {\n flex-wrap: nowrap !important;\n }\n\n .flex-sm-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n\n .gap-sm-0 {\n gap: 0 !important;\n }\n\n .gap-sm-1 {\n gap: 0.25rem !important;\n }\n\n .gap-sm-2 {\n gap: 0.5rem !important;\n }\n\n .gap-sm-3 {\n gap: 1rem !important;\n }\n\n .gap-sm-4 {\n gap: 1.5rem !important;\n }\n\n .gap-sm-5 {\n gap: 3rem !important;\n }\n\n .justify-content-sm-start {\n justify-content: flex-start !important;\n }\n\n .justify-content-sm-end {\n justify-content: flex-end !important;\n }\n\n .justify-content-sm-center {\n justify-content: center !important;\n }\n\n .justify-content-sm-between {\n justify-content: space-between !important;\n }\n\n .justify-content-sm-around {\n justify-content: space-around !important;\n }\n\n .justify-content-sm-evenly {\n justify-content: space-evenly !important;\n }\n\n .align-items-sm-start {\n align-items: flex-start !important;\n }\n\n .align-items-sm-end {\n align-items: flex-end !important;\n }\n\n .align-items-sm-center {\n align-items: center !important;\n }\n\n .align-items-sm-baseline {\n align-items: baseline !important;\n }\n\n .align-items-sm-stretch {\n align-items: stretch !important;\n }\n\n .align-content-sm-start {\n align-content: flex-start !important;\n }\n\n .align-content-sm-end {\n align-content: flex-end !important;\n }\n\n .align-content-sm-center {\n align-content: center !important;\n }\n\n .align-content-sm-between {\n align-content: space-between !important;\n }\n\n .align-content-sm-around {\n align-content: space-around !important;\n }\n\n .align-content-sm-stretch {\n align-content: stretch !important;\n }\n\n .align-self-sm-auto {\n align-self: auto !important;\n }\n\n .align-self-sm-start {\n align-self: flex-start !important;\n }\n\n .align-self-sm-end {\n align-self: flex-end !important;\n }\n\n .align-self-sm-center {\n align-self: center !important;\n }\n\n .align-self-sm-baseline {\n align-self: baseline !important;\n }\n\n .align-self-sm-stretch {\n align-self: stretch !important;\n }\n\n .order-sm-first {\n order: -1 !important;\n }\n\n .order-sm-0 {\n order: 0 !important;\n }\n\n .order-sm-1 {\n order: 1 !important;\n }\n\n .order-sm-2 {\n order: 2 !important;\n }\n\n .order-sm-3 {\n order: 3 !important;\n }\n\n .order-sm-4 {\n order: 4 !important;\n }\n\n .order-sm-5 {\n order: 5 !important;\n }\n\n .order-sm-last {\n order: 6 !important;\n }\n\n .m-sm-0 {\n margin: 0 !important;\n }\n\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n\n .m-sm-3 {\n margin: 1rem !important;\n }\n\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n\n .m-sm-5 {\n margin: 3rem !important;\n }\n\n .m-sm-auto {\n margin: auto !important;\n }\n\n .mx-sm-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n\n .mx-sm-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n\n .mx-sm-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n\n .mx-sm-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n\n .my-sm-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n\n .my-sm-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n\n .my-sm-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n\n .my-sm-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n\n .my-sm-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n\n .my-sm-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n\n .my-sm-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n\n .mt-sm-0 {\n margin-top: 0 !important;\n }\n\n .mt-sm-1 {\n margin-top: 0.25rem !important;\n }\n\n .mt-sm-2 {\n margin-top: 0.5rem !important;\n }\n\n .mt-sm-3 {\n margin-top: 1rem !important;\n }\n\n .mt-sm-4 {\n margin-top: 1.5rem !important;\n }\n\n .mt-sm-5 {\n margin-top: 3rem !important;\n }\n\n .mt-sm-auto {\n margin-top: auto !important;\n }\n\n .me-sm-0 {\n margin-right: 0 !important;\n }\n\n .me-sm-1 {\n margin-right: 0.25rem !important;\n }\n\n .me-sm-2 {\n margin-right: 0.5rem !important;\n }\n\n .me-sm-3 {\n margin-right: 1rem !important;\n }\n\n .me-sm-4 {\n margin-right: 1.5rem !important;\n }\n\n .me-sm-5 {\n margin-right: 3rem !important;\n }\n\n .me-sm-auto {\n margin-right: auto !important;\n }\n\n .mb-sm-0 {\n margin-bottom: 0 !important;\n }\n\n .mb-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n\n .mb-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n\n .mb-sm-3 {\n margin-bottom: 1rem !important;\n }\n\n .mb-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n\n .mb-sm-5 {\n margin-bottom: 3rem !important;\n }\n\n .mb-sm-auto {\n margin-bottom: auto !important;\n }\n\n .ms-sm-0 {\n margin-left: 0 !important;\n }\n\n .ms-sm-1 {\n margin-left: 0.25rem !important;\n }\n\n .ms-sm-2 {\n margin-left: 0.5rem !important;\n }\n\n .ms-sm-3 {\n margin-left: 1rem !important;\n }\n\n .ms-sm-4 {\n margin-left: 1.5rem !important;\n }\n\n .ms-sm-5 {\n margin-left: 3rem !important;\n }\n\n .ms-sm-auto {\n margin-left: auto !important;\n }\n\n .p-sm-0 {\n padding: 0 !important;\n }\n\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n\n .p-sm-3 {\n padding: 1rem !important;\n }\n\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n\n .p-sm-5 {\n padding: 3rem !important;\n }\n\n .px-sm-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n\n .px-sm-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n\n .px-sm-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n\n .px-sm-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n\n .px-sm-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n\n .px-sm-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n\n .py-sm-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n\n .py-sm-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n\n .py-sm-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n\n .py-sm-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n\n .py-sm-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n\n .py-sm-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n\n .pt-sm-0 {\n padding-top: 0 !important;\n }\n\n .pt-sm-1 {\n padding-top: 0.25rem !important;\n }\n\n .pt-sm-2 {\n padding-top: 0.5rem !important;\n }\n\n .pt-sm-3 {\n padding-top: 1rem !important;\n }\n\n .pt-sm-4 {\n padding-top: 1.5rem !important;\n }\n\n .pt-sm-5 {\n padding-top: 3rem !important;\n }\n\n .pe-sm-0 {\n padding-right: 0 !important;\n }\n\n .pe-sm-1 {\n padding-right: 0.25rem !important;\n }\n\n .pe-sm-2 {\n padding-right: 0.5rem !important;\n }\n\n .pe-sm-3 {\n padding-right: 1rem !important;\n }\n\n .pe-sm-4 {\n padding-right: 1.5rem !important;\n }\n\n .pe-sm-5 {\n padding-right: 3rem !important;\n }\n\n .pb-sm-0 {\n padding-bottom: 0 !important;\n }\n\n .pb-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n\n .pb-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n\n .pb-sm-3 {\n padding-bottom: 1rem !important;\n }\n\n .pb-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n\n .pb-sm-5 {\n padding-bottom: 3rem !important;\n }\n\n .ps-sm-0 {\n padding-left: 0 !important;\n }\n\n .ps-sm-1 {\n padding-left: 0.25rem !important;\n }\n\n .ps-sm-2 {\n padding-left: 0.5rem !important;\n }\n\n .ps-sm-3 {\n padding-left: 1rem !important;\n }\n\n .ps-sm-4 {\n padding-left: 1.5rem !important;\n }\n\n .ps-sm-5 {\n padding-left: 3rem !important;\n }\n\n .text-sm-start {\n text-align: left !important;\n }\n\n .text-sm-end {\n text-align: right !important;\n }\n\n .text-sm-center {\n text-align: center !important;\n }\n}\n@media (min-width: 768px) {\n .float-md-start {\n float: left !important;\n }\n\n .float-md-end {\n float: right !important;\n }\n\n .float-md-none {\n float: none !important;\n }\n\n .d-md-inline {\n display: inline !important;\n }\n\n .d-md-inline-block {\n display: inline-block !important;\n }\n\n .d-md-block {\n display: block !important;\n }\n\n .d-md-grid {\n display: grid !important;\n }\n\n .d-md-table {\n display: table !important;\n }\n\n .d-md-table-row {\n display: table-row !important;\n }\n\n .d-md-table-cell {\n display: table-cell !important;\n }\n\n .d-md-flex {\n display: flex !important;\n }\n\n .d-md-inline-flex {\n display: inline-flex !important;\n }\n\n .d-md-none {\n display: none !important;\n }\n\n .flex-md-fill {\n flex: 1 1 auto !important;\n }\n\n .flex-md-row {\n flex-direction: row !important;\n }\n\n .flex-md-column {\n flex-direction: column !important;\n }\n\n .flex-md-row-reverse {\n flex-direction: row-reverse !important;\n }\n\n .flex-md-column-reverse {\n flex-direction: column-reverse !important;\n }\n\n .flex-md-grow-0 {\n flex-grow: 0 !important;\n }\n\n .flex-md-grow-1 {\n flex-grow: 1 !important;\n }\n\n .flex-md-shrink-0 {\n flex-shrink: 0 !important;\n }\n\n .flex-md-shrink-1 {\n flex-shrink: 1 !important;\n }\n\n .flex-md-wrap {\n flex-wrap: wrap !important;\n }\n\n .flex-md-nowrap {\n flex-wrap: nowrap !important;\n }\n\n .flex-md-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n\n .gap-md-0 {\n gap: 0 !important;\n }\n\n .gap-md-1 {\n gap: 0.25rem !important;\n }\n\n .gap-md-2 {\n gap: 0.5rem !important;\n }\n\n .gap-md-3 {\n gap: 1rem !important;\n }\n\n .gap-md-4 {\n gap: 1.5rem !important;\n }\n\n .gap-md-5 {\n gap: 3rem !important;\n }\n\n .justify-content-md-start {\n justify-content: flex-start !important;\n }\n\n .justify-content-md-end {\n justify-content: flex-end !important;\n }\n\n .justify-content-md-center {\n justify-content: center !important;\n }\n\n .justify-content-md-between {\n justify-content: space-between !important;\n }\n\n .justify-content-md-around {\n justify-content: space-around !important;\n }\n\n .justify-content-md-evenly {\n justify-content: space-evenly !important;\n }\n\n .align-items-md-start {\n align-items: flex-start !important;\n }\n\n .align-items-md-end {\n align-items: flex-end !important;\n }\n\n .align-items-md-center {\n align-items: center !important;\n }\n\n .align-items-md-baseline {\n align-items: baseline !important;\n }\n\n .align-items-md-stretch {\n align-items: stretch !important;\n }\n\n .align-content-md-start {\n align-content: flex-start !important;\n }\n\n .align-content-md-end {\n align-content: flex-end !important;\n }\n\n .align-content-md-center {\n align-content: center !important;\n }\n\n .align-content-md-between {\n align-content: space-between !important;\n }\n\n .align-content-md-around {\n align-content: space-around !important;\n }\n\n .align-content-md-stretch {\n align-content: stretch !important;\n }\n\n .align-self-md-auto {\n align-self: auto !important;\n }\n\n .align-self-md-start {\n align-self: flex-start !important;\n }\n\n .align-self-md-end {\n align-self: flex-end !important;\n }\n\n .align-self-md-center {\n align-self: center !important;\n }\n\n .align-self-md-baseline {\n align-self: baseline !important;\n }\n\n .align-self-md-stretch {\n align-self: stretch !important;\n }\n\n .order-md-first {\n order: -1 !important;\n }\n\n .order-md-0 {\n order: 0 !important;\n }\n\n .order-md-1 {\n order: 1 !important;\n }\n\n .order-md-2 {\n order: 2 !important;\n }\n\n .order-md-3 {\n order: 3 !important;\n }\n\n .order-md-4 {\n order: 4 !important;\n }\n\n .order-md-5 {\n order: 5 !important;\n }\n\n .order-md-last {\n order: 6 !important;\n }\n\n .m-md-0 {\n margin: 0 !important;\n }\n\n .m-md-1 {\n margin: 0.25rem !important;\n }\n\n .m-md-2 {\n margin: 0.5rem !important;\n }\n\n .m-md-3 {\n margin: 1rem !important;\n }\n\n .m-md-4 {\n margin: 1.5rem !important;\n }\n\n .m-md-5 {\n margin: 3rem !important;\n }\n\n .m-md-auto {\n margin: auto !important;\n }\n\n .mx-md-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n\n .mx-md-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n\n .mx-md-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n\n .mx-md-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n\n .mx-md-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n\n .mx-md-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n\n .mx-md-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n\n .my-md-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n\n .my-md-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n\n .my-md-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n\n .my-md-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n\n .my-md-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n\n .my-md-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n\n .my-md-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n\n .mt-md-0 {\n margin-top: 0 !important;\n }\n\n .mt-md-1 {\n margin-top: 0.25rem !important;\n }\n\n .mt-md-2 {\n margin-top: 0.5rem !important;\n }\n\n .mt-md-3 {\n margin-top: 1rem !important;\n }\n\n .mt-md-4 {\n margin-top: 1.5rem !important;\n }\n\n .mt-md-5 {\n margin-top: 3rem !important;\n }\n\n .mt-md-auto {\n margin-top: auto !important;\n }\n\n .me-md-0 {\n margin-right: 0 !important;\n }\n\n .me-md-1 {\n margin-right: 0.25rem !important;\n }\n\n .me-md-2 {\n margin-right: 0.5rem !important;\n }\n\n .me-md-3 {\n margin-right: 1rem !important;\n }\n\n .me-md-4 {\n margin-right: 1.5rem !important;\n }\n\n .me-md-5 {\n margin-right: 3rem !important;\n }\n\n .me-md-auto {\n margin-right: auto !important;\n }\n\n .mb-md-0 {\n margin-bottom: 0 !important;\n }\n\n .mb-md-1 {\n margin-bottom: 0.25rem !important;\n }\n\n .mb-md-2 {\n margin-bottom: 0.5rem !important;\n }\n\n .mb-md-3 {\n margin-bottom: 1rem !important;\n }\n\n .mb-md-4 {\n margin-bottom: 1.5rem !important;\n }\n\n .mb-md-5 {\n margin-bottom: 3rem !important;\n }\n\n .mb-md-auto {\n margin-bottom: auto !important;\n }\n\n .ms-md-0 {\n margin-left: 0 !important;\n }\n\n .ms-md-1 {\n margin-left: 0.25rem !important;\n }\n\n .ms-md-2 {\n margin-left: 0.5rem !important;\n }\n\n .ms-md-3 {\n margin-left: 1rem !important;\n }\n\n .ms-md-4 {\n margin-left: 1.5rem !important;\n }\n\n .ms-md-5 {\n margin-left: 3rem !important;\n }\n\n .ms-md-auto {\n margin-left: auto !important;\n }\n\n .p-md-0 {\n padding: 0 !important;\n }\n\n .p-md-1 {\n padding: 0.25rem !important;\n }\n\n .p-md-2 {\n padding: 0.5rem !important;\n }\n\n .p-md-3 {\n padding: 1rem !important;\n }\n\n .p-md-4 {\n padding: 1.5rem !important;\n }\n\n .p-md-5 {\n padding: 3rem !important;\n }\n\n .px-md-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n\n .px-md-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n\n .px-md-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n\n .px-md-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n\n .px-md-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n\n .px-md-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n\n .py-md-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n\n .py-md-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n\n .py-md-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n\n .py-md-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n\n .py-md-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n\n .py-md-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n\n .pt-md-0 {\n padding-top: 0 !important;\n }\n\n .pt-md-1 {\n padding-top: 0.25rem !important;\n }\n\n .pt-md-2 {\n padding-top: 0.5rem !important;\n }\n\n .pt-md-3 {\n padding-top: 1rem !important;\n }\n\n .pt-md-4 {\n padding-top: 1.5rem !important;\n }\n\n .pt-md-5 {\n padding-top: 3rem !important;\n }\n\n .pe-md-0 {\n padding-right: 0 !important;\n }\n\n .pe-md-1 {\n padding-right: 0.25rem !important;\n }\n\n .pe-md-2 {\n padding-right: 0.5rem !important;\n }\n\n .pe-md-3 {\n padding-right: 1rem !important;\n }\n\n .pe-md-4 {\n padding-right: 1.5rem !important;\n }\n\n .pe-md-5 {\n padding-right: 3rem !important;\n }\n\n .pb-md-0 {\n padding-bottom: 0 !important;\n }\n\n .pb-md-1 {\n padding-bottom: 0.25rem !important;\n }\n\n .pb-md-2 {\n padding-bottom: 0.5rem !important;\n }\n\n .pb-md-3 {\n padding-bottom: 1rem !important;\n }\n\n .pb-md-4 {\n padding-bottom: 1.5rem !important;\n }\n\n .pb-md-5 {\n padding-bottom: 3rem !important;\n }\n\n .ps-md-0 {\n padding-left: 0 !important;\n }\n\n .ps-md-1 {\n padding-left: 0.25rem !important;\n }\n\n .ps-md-2 {\n padding-left: 0.5rem !important;\n }\n\n .ps-md-3 {\n padding-left: 1rem !important;\n }\n\n .ps-md-4 {\n padding-left: 1.5rem !important;\n }\n\n .ps-md-5 {\n padding-left: 3rem !important;\n }\n\n .text-md-start {\n text-align: left !important;\n }\n\n .text-md-end {\n text-align: right !important;\n }\n\n .text-md-center {\n text-align: center !important;\n }\n}\n@media (min-width: 992px) {\n .float-lg-start {\n float: left !important;\n }\n\n .float-lg-end {\n float: right !important;\n }\n\n .float-lg-none {\n float: none !important;\n }\n\n .d-lg-inline {\n display: inline !important;\n }\n\n .d-lg-inline-block {\n display: inline-block !important;\n }\n\n .d-lg-block {\n display: block !important;\n }\n\n .d-lg-grid {\n display: grid !important;\n }\n\n .d-lg-table {\n display: table !important;\n }\n\n .d-lg-table-row {\n display: table-row !important;\n }\n\n .d-lg-table-cell {\n display: table-cell !important;\n }\n\n .d-lg-flex {\n display: flex !important;\n }\n\n .d-lg-inline-flex {\n display: inline-flex !important;\n }\n\n .d-lg-none {\n display: none !important;\n }\n\n .flex-lg-fill {\n flex: 1 1 auto !important;\n }\n\n .flex-lg-row {\n flex-direction: row !important;\n }\n\n .flex-lg-column {\n flex-direction: column !important;\n }\n\n .flex-lg-row-reverse {\n flex-direction: row-reverse !important;\n }\n\n .flex-lg-column-reverse {\n flex-direction: column-reverse !important;\n }\n\n .flex-lg-grow-0 {\n flex-grow: 0 !important;\n }\n\n .flex-lg-grow-1 {\n flex-grow: 1 !important;\n }\n\n .flex-lg-shrink-0 {\n flex-shrink: 0 !important;\n }\n\n .flex-lg-shrink-1 {\n flex-shrink: 1 !important;\n }\n\n .flex-lg-wrap {\n flex-wrap: wrap !important;\n }\n\n .flex-lg-nowrap {\n flex-wrap: nowrap !important;\n }\n\n .flex-lg-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n\n .gap-lg-0 {\n gap: 0 !important;\n }\n\n .gap-lg-1 {\n gap: 0.25rem !important;\n }\n\n .gap-lg-2 {\n gap: 0.5rem !important;\n }\n\n .gap-lg-3 {\n gap: 1rem !important;\n }\n\n .gap-lg-4 {\n gap: 1.5rem !important;\n }\n\n .gap-lg-5 {\n gap: 3rem !important;\n }\n\n .justify-content-lg-start {\n justify-content: flex-start !important;\n }\n\n .justify-content-lg-end {\n justify-content: flex-end !important;\n }\n\n .justify-content-lg-center {\n justify-content: center !important;\n }\n\n .justify-content-lg-between {\n justify-content: space-between !important;\n }\n\n .justify-content-lg-around {\n justify-content: space-around !important;\n }\n\n .justify-content-lg-evenly {\n justify-content: space-evenly !important;\n }\n\n .align-items-lg-start {\n align-items: flex-start !important;\n }\n\n .align-items-lg-end {\n align-items: flex-end !important;\n }\n\n .align-items-lg-center {\n align-items: center !important;\n }\n\n .align-items-lg-baseline {\n align-items: baseline !important;\n }\n\n .align-items-lg-stretch {\n align-items: stretch !important;\n }\n\n .align-content-lg-start {\n align-content: flex-start !important;\n }\n\n .align-content-lg-end {\n align-content: flex-end !important;\n }\n\n .align-content-lg-center {\n align-content: center !important;\n }\n\n .align-content-lg-between {\n align-content: space-between !important;\n }\n\n .align-content-lg-around {\n align-content: space-around !important;\n }\n\n .align-content-lg-stretch {\n align-content: stretch !important;\n }\n\n .align-self-lg-auto {\n align-self: auto !important;\n }\n\n .align-self-lg-start {\n align-self: flex-start !important;\n }\n\n .align-self-lg-end {\n align-self: flex-end !important;\n }\n\n .align-self-lg-center {\n align-self: center !important;\n }\n\n .align-self-lg-baseline {\n align-self: baseline !important;\n }\n\n .align-self-lg-stretch {\n align-self: stretch !important;\n }\n\n .order-lg-first {\n order: -1 !important;\n }\n\n .order-lg-0 {\n order: 0 !important;\n }\n\n .order-lg-1 {\n order: 1 !important;\n }\n\n .order-lg-2 {\n order: 2 !important;\n }\n\n .order-lg-3 {\n order: 3 !important;\n }\n\n .order-lg-4 {\n order: 4 !important;\n }\n\n .order-lg-5 {\n order: 5 !important;\n }\n\n .order-lg-last {\n order: 6 !important;\n }\n\n .m-lg-0 {\n margin: 0 !important;\n }\n\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n\n .m-lg-3 {\n margin: 1rem !important;\n }\n\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n\n .m-lg-5 {\n margin: 3rem !important;\n }\n\n .m-lg-auto {\n margin: auto !important;\n }\n\n .mx-lg-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n\n .mx-lg-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n\n .mx-lg-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n\n .mx-lg-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n\n .my-lg-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n\n .my-lg-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n\n .my-lg-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n\n .my-lg-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n\n .my-lg-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n\n .my-lg-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n\n .my-lg-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n\n .mt-lg-0 {\n margin-top: 0 !important;\n }\n\n .mt-lg-1 {\n margin-top: 0.25rem !important;\n }\n\n .mt-lg-2 {\n margin-top: 0.5rem !important;\n }\n\n .mt-lg-3 {\n margin-top: 1rem !important;\n }\n\n .mt-lg-4 {\n margin-top: 1.5rem !important;\n }\n\n .mt-lg-5 {\n margin-top: 3rem !important;\n }\n\n .mt-lg-auto {\n margin-top: auto !important;\n }\n\n .me-lg-0 {\n margin-right: 0 !important;\n }\n\n .me-lg-1 {\n margin-right: 0.25rem !important;\n }\n\n .me-lg-2 {\n margin-right: 0.5rem !important;\n }\n\n .me-lg-3 {\n margin-right: 1rem !important;\n }\n\n .me-lg-4 {\n margin-right: 1.5rem !important;\n }\n\n .me-lg-5 {\n margin-right: 3rem !important;\n }\n\n .me-lg-auto {\n margin-right: auto !important;\n }\n\n .mb-lg-0 {\n margin-bottom: 0 !important;\n }\n\n .mb-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n\n .mb-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n\n .mb-lg-3 {\n margin-bottom: 1rem !important;\n }\n\n .mb-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n\n .mb-lg-5 {\n margin-bottom: 3rem !important;\n }\n\n .mb-lg-auto {\n margin-bottom: auto !important;\n }\n\n .ms-lg-0 {\n margin-left: 0 !important;\n }\n\n .ms-lg-1 {\n margin-left: 0.25rem !important;\n }\n\n .ms-lg-2 {\n margin-left: 0.5rem !important;\n }\n\n .ms-lg-3 {\n margin-left: 1rem !important;\n }\n\n .ms-lg-4 {\n margin-left: 1.5rem !important;\n }\n\n .ms-lg-5 {\n margin-left: 3rem !important;\n }\n\n .ms-lg-auto {\n margin-left: auto !important;\n }\n\n .p-lg-0 {\n padding: 0 !important;\n }\n\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n\n .p-lg-3 {\n padding: 1rem !important;\n }\n\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n\n .p-lg-5 {\n padding: 3rem !important;\n }\n\n .px-lg-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n\n .px-lg-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n\n .px-lg-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n\n .px-lg-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n\n .px-lg-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n\n .px-lg-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n\n .py-lg-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n\n .py-lg-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n\n .py-lg-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n\n .py-lg-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n\n .py-lg-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n\n .py-lg-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n\n .pt-lg-0 {\n padding-top: 0 !important;\n }\n\n .pt-lg-1 {\n padding-top: 0.25rem !important;\n }\n\n .pt-lg-2 {\n padding-top: 0.5rem !important;\n }\n\n .pt-lg-3 {\n padding-top: 1rem !important;\n }\n\n .pt-lg-4 {\n padding-top: 1.5rem !important;\n }\n\n .pt-lg-5 {\n padding-top: 3rem !important;\n }\n\n .pe-lg-0 {\n padding-right: 0 !important;\n }\n\n .pe-lg-1 {\n padding-right: 0.25rem !important;\n }\n\n .pe-lg-2 {\n padding-right: 0.5rem !important;\n }\n\n .pe-lg-3 {\n padding-right: 1rem !important;\n }\n\n .pe-lg-4 {\n padding-right: 1.5rem !important;\n }\n\n .pe-lg-5 {\n padding-right: 3rem !important;\n }\n\n .pb-lg-0 {\n padding-bottom: 0 !important;\n }\n\n .pb-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n\n .pb-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n\n .pb-lg-3 {\n padding-bottom: 1rem !important;\n }\n\n .pb-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n\n .pb-lg-5 {\n padding-bottom: 3rem !important;\n }\n\n .ps-lg-0 {\n padding-left: 0 !important;\n }\n\n .ps-lg-1 {\n padding-left: 0.25rem !important;\n }\n\n .ps-lg-2 {\n padding-left: 0.5rem !important;\n }\n\n .ps-lg-3 {\n padding-left: 1rem !important;\n }\n\n .ps-lg-4 {\n padding-left: 1.5rem !important;\n }\n\n .ps-lg-5 {\n padding-left: 3rem !important;\n }\n\n .text-lg-start {\n text-align: left !important;\n }\n\n .text-lg-end {\n text-align: right !important;\n }\n\n .text-lg-center {\n text-align: center !important;\n }\n}\n@media (min-width: 1200px) {\n .float-xl-start {\n float: left !important;\n }\n\n .float-xl-end {\n float: right !important;\n }\n\n .float-xl-none {\n float: none !important;\n }\n\n .d-xl-inline {\n display: inline !important;\n }\n\n .d-xl-inline-block {\n display: inline-block !important;\n }\n\n .d-xl-block {\n display: block !important;\n }\n\n .d-xl-grid {\n display: grid !important;\n }\n\n .d-xl-table {\n display: table !important;\n }\n\n .d-xl-table-row {\n display: table-row !important;\n }\n\n .d-xl-table-cell {\n display: table-cell !important;\n }\n\n .d-xl-flex {\n display: flex !important;\n }\n\n .d-xl-inline-flex {\n display: inline-flex !important;\n }\n\n .d-xl-none {\n display: none !important;\n }\n\n .flex-xl-fill {\n flex: 1 1 auto !important;\n }\n\n .flex-xl-row {\n flex-direction: row !important;\n }\n\n .flex-xl-column {\n flex-direction: column !important;\n }\n\n .flex-xl-row-reverse {\n flex-direction: row-reverse !important;\n }\n\n .flex-xl-column-reverse {\n flex-direction: column-reverse !important;\n }\n\n .flex-xl-grow-0 {\n flex-grow: 0 !important;\n }\n\n .flex-xl-grow-1 {\n flex-grow: 1 !important;\n }\n\n .flex-xl-shrink-0 {\n flex-shrink: 0 !important;\n }\n\n .flex-xl-shrink-1 {\n flex-shrink: 1 !important;\n }\n\n .flex-xl-wrap {\n flex-wrap: wrap !important;\n }\n\n .flex-xl-nowrap {\n flex-wrap: nowrap !important;\n }\n\n .flex-xl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n\n .gap-xl-0 {\n gap: 0 !important;\n }\n\n .gap-xl-1 {\n gap: 0.25rem !important;\n }\n\n .gap-xl-2 {\n gap: 0.5rem !important;\n }\n\n .gap-xl-3 {\n gap: 1rem !important;\n }\n\n .gap-xl-4 {\n gap: 1.5rem !important;\n }\n\n .gap-xl-5 {\n gap: 3rem !important;\n }\n\n .justify-content-xl-start {\n justify-content: flex-start !important;\n }\n\n .justify-content-xl-end {\n justify-content: flex-end !important;\n }\n\n .justify-content-xl-center {\n justify-content: center !important;\n }\n\n .justify-content-xl-between {\n justify-content: space-between !important;\n }\n\n .justify-content-xl-around {\n justify-content: space-around !important;\n }\n\n .justify-content-xl-evenly {\n justify-content: space-evenly !important;\n }\n\n .align-items-xl-start {\n align-items: flex-start !important;\n }\n\n .align-items-xl-end {\n align-items: flex-end !important;\n }\n\n .align-items-xl-center {\n align-items: center !important;\n }\n\n .align-items-xl-baseline {\n align-items: baseline !important;\n }\n\n .align-items-xl-stretch {\n align-items: stretch !important;\n }\n\n .align-content-xl-start {\n align-content: flex-start !important;\n }\n\n .align-content-xl-end {\n align-content: flex-end !important;\n }\n\n .align-content-xl-center {\n align-content: center !important;\n }\n\n .align-content-xl-between {\n align-content: space-between !important;\n }\n\n .align-content-xl-around {\n align-content: space-around !important;\n }\n\n .align-content-xl-stretch {\n align-content: stretch !important;\n }\n\n .align-self-xl-auto {\n align-self: auto !important;\n }\n\n .align-self-xl-start {\n align-self: flex-start !important;\n }\n\n .align-self-xl-end {\n align-self: flex-end !important;\n }\n\n .align-self-xl-center {\n align-self: center !important;\n }\n\n .align-self-xl-baseline {\n align-self: baseline !important;\n }\n\n .align-self-xl-stretch {\n align-self: stretch !important;\n }\n\n .order-xl-first {\n order: -1 !important;\n }\n\n .order-xl-0 {\n order: 0 !important;\n }\n\n .order-xl-1 {\n order: 1 !important;\n }\n\n .order-xl-2 {\n order: 2 !important;\n }\n\n .order-xl-3 {\n order: 3 !important;\n }\n\n .order-xl-4 {\n order: 4 !important;\n }\n\n .order-xl-5 {\n order: 5 !important;\n }\n\n .order-xl-last {\n order: 6 !important;\n }\n\n .m-xl-0 {\n margin: 0 !important;\n }\n\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n\n .m-xl-3 {\n margin: 1rem !important;\n }\n\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n\n .m-xl-5 {\n margin: 3rem !important;\n }\n\n .m-xl-auto {\n margin: auto !important;\n }\n\n .mx-xl-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n\n .mx-xl-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n\n .mx-xl-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n\n .mx-xl-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n\n .my-xl-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n\n .my-xl-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n\n .my-xl-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n\n .my-xl-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n\n .my-xl-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n\n .my-xl-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n\n .my-xl-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n\n .mt-xl-0 {\n margin-top: 0 !important;\n }\n\n .mt-xl-1 {\n margin-top: 0.25rem !important;\n }\n\n .mt-xl-2 {\n margin-top: 0.5rem !important;\n }\n\n .mt-xl-3 {\n margin-top: 1rem !important;\n }\n\n .mt-xl-4 {\n margin-top: 1.5rem !important;\n }\n\n .mt-xl-5 {\n margin-top: 3rem !important;\n }\n\n .mt-xl-auto {\n margin-top: auto !important;\n }\n\n .me-xl-0 {\n margin-right: 0 !important;\n }\n\n .me-xl-1 {\n margin-right: 0.25rem !important;\n }\n\n .me-xl-2 {\n margin-right: 0.5rem !important;\n }\n\n .me-xl-3 {\n margin-right: 1rem !important;\n }\n\n .me-xl-4 {\n margin-right: 1.5rem !important;\n }\n\n .me-xl-5 {\n margin-right: 3rem !important;\n }\n\n .me-xl-auto {\n margin-right: auto !important;\n }\n\n .mb-xl-0 {\n margin-bottom: 0 !important;\n }\n\n .mb-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n\n .mb-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n\n .mb-xl-3 {\n margin-bottom: 1rem !important;\n }\n\n .mb-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n\n .mb-xl-5 {\n margin-bottom: 3rem !important;\n }\n\n .mb-xl-auto {\n margin-bottom: auto !important;\n }\n\n .ms-xl-0 {\n margin-left: 0 !important;\n }\n\n .ms-xl-1 {\n margin-left: 0.25rem !important;\n }\n\n .ms-xl-2 {\n margin-left: 0.5rem !important;\n }\n\n .ms-xl-3 {\n margin-left: 1rem !important;\n }\n\n .ms-xl-4 {\n margin-left: 1.5rem !important;\n }\n\n .ms-xl-5 {\n margin-left: 3rem !important;\n }\n\n .ms-xl-auto {\n margin-left: auto !important;\n }\n\n .p-xl-0 {\n padding: 0 !important;\n }\n\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n\n .p-xl-3 {\n padding: 1rem !important;\n }\n\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n\n .p-xl-5 {\n padding: 3rem !important;\n }\n\n .px-xl-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n\n .px-xl-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n\n .px-xl-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n\n .px-xl-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n\n .px-xl-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n\n .px-xl-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n\n .py-xl-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n\n .py-xl-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n\n .py-xl-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n\n .py-xl-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n\n .py-xl-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n\n .py-xl-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n\n .pt-xl-0 {\n padding-top: 0 !important;\n }\n\n .pt-xl-1 {\n padding-top: 0.25rem !important;\n }\n\n .pt-xl-2 {\n padding-top: 0.5rem !important;\n }\n\n .pt-xl-3 {\n padding-top: 1rem !important;\n }\n\n .pt-xl-4 {\n padding-top: 1.5rem !important;\n }\n\n .pt-xl-5 {\n padding-top: 3rem !important;\n }\n\n .pe-xl-0 {\n padding-right: 0 !important;\n }\n\n .pe-xl-1 {\n padding-right: 0.25rem !important;\n }\n\n .pe-xl-2 {\n padding-right: 0.5rem !important;\n }\n\n .pe-xl-3 {\n padding-right: 1rem !important;\n }\n\n .pe-xl-4 {\n padding-right: 1.5rem !important;\n }\n\n .pe-xl-5 {\n padding-right: 3rem !important;\n }\n\n .pb-xl-0 {\n padding-bottom: 0 !important;\n }\n\n .pb-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n\n .pb-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n\n .pb-xl-3 {\n padding-bottom: 1rem !important;\n }\n\n .pb-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n\n .pb-xl-5 {\n padding-bottom: 3rem !important;\n }\n\n .ps-xl-0 {\n padding-left: 0 !important;\n }\n\n .ps-xl-1 {\n padding-left: 0.25rem !important;\n }\n\n .ps-xl-2 {\n padding-left: 0.5rem !important;\n }\n\n .ps-xl-3 {\n padding-left: 1rem !important;\n }\n\n .ps-xl-4 {\n padding-left: 1.5rem !important;\n }\n\n .ps-xl-5 {\n padding-left: 3rem !important;\n }\n\n .text-xl-start {\n text-align: left !important;\n }\n\n .text-xl-end {\n text-align: right !important;\n }\n\n .text-xl-center {\n text-align: center !important;\n }\n}\n@media (min-width: 1400px) {\n .float-xxl-start {\n float: left !important;\n }\n\n .float-xxl-end {\n float: right !important;\n }\n\n .float-xxl-none {\n float: none !important;\n }\n\n .d-xxl-inline {\n display: inline !important;\n }\n\n .d-xxl-inline-block {\n display: inline-block !important;\n }\n\n .d-xxl-block {\n display: block !important;\n }\n\n .d-xxl-grid {\n display: grid !important;\n }\n\n .d-xxl-table {\n display: table !important;\n }\n\n .d-xxl-table-row {\n display: table-row !important;\n }\n\n .d-xxl-table-cell {\n display: table-cell !important;\n }\n\n .d-xxl-flex {\n display: flex !important;\n }\n\n .d-xxl-inline-flex {\n display: inline-flex !important;\n }\n\n .d-xxl-none {\n display: none !important;\n }\n\n .flex-xxl-fill {\n flex: 1 1 auto !important;\n }\n\n .flex-xxl-row {\n flex-direction: row !important;\n }\n\n .flex-xxl-column {\n flex-direction: column !important;\n }\n\n .flex-xxl-row-reverse {\n flex-direction: row-reverse !important;\n }\n\n .flex-xxl-column-reverse {\n flex-direction: column-reverse !important;\n }\n\n .flex-xxl-grow-0 {\n flex-grow: 0 !important;\n }\n\n .flex-xxl-grow-1 {\n flex-grow: 1 !important;\n }\n\n .flex-xxl-shrink-0 {\n flex-shrink: 0 !important;\n }\n\n .flex-xxl-shrink-1 {\n flex-shrink: 1 !important;\n }\n\n .flex-xxl-wrap {\n flex-wrap: wrap !important;\n }\n\n .flex-xxl-nowrap {\n flex-wrap: nowrap !important;\n }\n\n .flex-xxl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n\n .gap-xxl-0 {\n gap: 0 !important;\n }\n\n .gap-xxl-1 {\n gap: 0.25rem !important;\n }\n\n .gap-xxl-2 {\n gap: 0.5rem !important;\n }\n\n .gap-xxl-3 {\n gap: 1rem !important;\n }\n\n .gap-xxl-4 {\n gap: 1.5rem !important;\n }\n\n .gap-xxl-5 {\n gap: 3rem !important;\n }\n\n .justify-content-xxl-start {\n justify-content: flex-start !important;\n }\n\n .justify-content-xxl-end {\n justify-content: flex-end !important;\n }\n\n .justify-content-xxl-center {\n justify-content: center !important;\n }\n\n .justify-content-xxl-between {\n justify-content: space-between !important;\n }\n\n .justify-content-xxl-around {\n justify-content: space-around !important;\n }\n\n .justify-content-xxl-evenly {\n justify-content: space-evenly !important;\n }\n\n .align-items-xxl-start {\n align-items: flex-start !important;\n }\n\n .align-items-xxl-end {\n align-items: flex-end !important;\n }\n\n .align-items-xxl-center {\n align-items: center !important;\n }\n\n .align-items-xxl-baseline {\n align-items: baseline !important;\n }\n\n .align-items-xxl-stretch {\n align-items: stretch !important;\n }\n\n .align-content-xxl-start {\n align-content: flex-start !important;\n }\n\n .align-content-xxl-end {\n align-content: flex-end !important;\n }\n\n .align-content-xxl-center {\n align-content: center !important;\n }\n\n .align-content-xxl-between {\n align-content: space-between !important;\n }\n\n .align-content-xxl-around {\n align-content: space-around !important;\n }\n\n .align-content-xxl-stretch {\n align-content: stretch !important;\n }\n\n .align-self-xxl-auto {\n align-self: auto !important;\n }\n\n .align-self-xxl-start {\n align-self: flex-start !important;\n }\n\n .align-self-xxl-end {\n align-self: flex-end !important;\n }\n\n .align-self-xxl-center {\n align-self: center !important;\n }\n\n .align-self-xxl-baseline {\n align-self: baseline !important;\n }\n\n .align-self-xxl-stretch {\n align-self: stretch !important;\n }\n\n .order-xxl-first {\n order: -1 !important;\n }\n\n .order-xxl-0 {\n order: 0 !important;\n }\n\n .order-xxl-1 {\n order: 1 !important;\n }\n\n .order-xxl-2 {\n order: 2 !important;\n }\n\n .order-xxl-3 {\n order: 3 !important;\n }\n\n .order-xxl-4 {\n order: 4 !important;\n }\n\n .order-xxl-5 {\n order: 5 !important;\n }\n\n .order-xxl-last {\n order: 6 !important;\n }\n\n .m-xxl-0 {\n margin: 0 !important;\n }\n\n .m-xxl-1 {\n margin: 0.25rem !important;\n }\n\n .m-xxl-2 {\n margin: 0.5rem !important;\n }\n\n .m-xxl-3 {\n margin: 1rem !important;\n }\n\n .m-xxl-4 {\n margin: 1.5rem !important;\n }\n\n .m-xxl-5 {\n margin: 3rem !important;\n }\n\n .m-xxl-auto {\n margin: auto !important;\n }\n\n .mx-xxl-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n\n .mx-xxl-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n\n .mx-xxl-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n\n .mx-xxl-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n\n .mx-xxl-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n\n .mx-xxl-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n\n .mx-xxl-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n\n .my-xxl-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n\n .my-xxl-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n\n .my-xxl-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n\n .my-xxl-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n\n .my-xxl-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n\n .my-xxl-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n\n .my-xxl-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n\n .mt-xxl-0 {\n margin-top: 0 !important;\n }\n\n .mt-xxl-1 {\n margin-top: 0.25rem !important;\n }\n\n .mt-xxl-2 {\n margin-top: 0.5rem !important;\n }\n\n .mt-xxl-3 {\n margin-top: 1rem !important;\n }\n\n .mt-xxl-4 {\n margin-top: 1.5rem !important;\n }\n\n .mt-xxl-5 {\n margin-top: 3rem !important;\n }\n\n .mt-xxl-auto {\n margin-top: auto !important;\n }\n\n .me-xxl-0 {\n margin-right: 0 !important;\n }\n\n .me-xxl-1 {\n margin-right: 0.25rem !important;\n }\n\n .me-xxl-2 {\n margin-right: 0.5rem !important;\n }\n\n .me-xxl-3 {\n margin-right: 1rem !important;\n }\n\n .me-xxl-4 {\n margin-right: 1.5rem !important;\n }\n\n .me-xxl-5 {\n margin-right: 3rem !important;\n }\n\n .me-xxl-auto {\n margin-right: auto !important;\n }\n\n .mb-xxl-0 {\n margin-bottom: 0 !important;\n }\n\n .mb-xxl-1 {\n margin-bottom: 0.25rem !important;\n }\n\n .mb-xxl-2 {\n margin-bottom: 0.5rem !important;\n }\n\n .mb-xxl-3 {\n margin-bottom: 1rem !important;\n }\n\n .mb-xxl-4 {\n margin-bottom: 1.5rem !important;\n }\n\n .mb-xxl-5 {\n margin-bottom: 3rem !important;\n }\n\n .mb-xxl-auto {\n margin-bottom: auto !important;\n }\n\n .ms-xxl-0 {\n margin-left: 0 !important;\n }\n\n .ms-xxl-1 {\n margin-left: 0.25rem !important;\n }\n\n .ms-xxl-2 {\n margin-left: 0.5rem !important;\n }\n\n .ms-xxl-3 {\n margin-left: 1rem !important;\n }\n\n .ms-xxl-4 {\n margin-left: 1.5rem !important;\n }\n\n .ms-xxl-5 {\n margin-left: 3rem !important;\n }\n\n .ms-xxl-auto {\n margin-left: auto !important;\n }\n\n .p-xxl-0 {\n padding: 0 !important;\n }\n\n .p-xxl-1 {\n padding: 0.25rem !important;\n }\n\n .p-xxl-2 {\n padding: 0.5rem !important;\n }\n\n .p-xxl-3 {\n padding: 1rem !important;\n }\n\n .p-xxl-4 {\n padding: 1.5rem !important;\n }\n\n .p-xxl-5 {\n padding: 3rem !important;\n }\n\n .px-xxl-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n\n .px-xxl-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n\n .px-xxl-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n\n .px-xxl-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n\n .px-xxl-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n\n .px-xxl-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n\n .py-xxl-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n\n .py-xxl-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n\n .py-xxl-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n\n .py-xxl-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n\n .py-xxl-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n\n .py-xxl-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n\n .pt-xxl-0 {\n padding-top: 0 !important;\n }\n\n .pt-xxl-1 {\n padding-top: 0.25rem !important;\n }\n\n .pt-xxl-2 {\n padding-top: 0.5rem !important;\n }\n\n .pt-xxl-3 {\n padding-top: 1rem !important;\n }\n\n .pt-xxl-4 {\n padding-top: 1.5rem !important;\n }\n\n .pt-xxl-5 {\n padding-top: 3rem !important;\n }\n\n .pe-xxl-0 {\n padding-right: 0 !important;\n }\n\n .pe-xxl-1 {\n padding-right: 0.25rem !important;\n }\n\n .pe-xxl-2 {\n padding-right: 0.5rem !important;\n }\n\n .pe-xxl-3 {\n padding-right: 1rem !important;\n }\n\n .pe-xxl-4 {\n padding-right: 1.5rem !important;\n }\n\n .pe-xxl-5 {\n padding-right: 3rem !important;\n }\n\n .pb-xxl-0 {\n padding-bottom: 0 !important;\n }\n\n .pb-xxl-1 {\n padding-bottom: 0.25rem !important;\n }\n\n .pb-xxl-2 {\n padding-bottom: 0.5rem !important;\n }\n\n .pb-xxl-3 {\n padding-bottom: 1rem !important;\n }\n\n .pb-xxl-4 {\n padding-bottom: 1.5rem !important;\n }\n\n .pb-xxl-5 {\n padding-bottom: 3rem !important;\n }\n\n .ps-xxl-0 {\n padding-left: 0 !important;\n }\n\n .ps-xxl-1 {\n padding-left: 0.25rem !important;\n }\n\n .ps-xxl-2 {\n padding-left: 0.5rem !important;\n }\n\n .ps-xxl-3 {\n padding-left: 1rem !important;\n }\n\n .ps-xxl-4 {\n padding-left: 1.5rem !important;\n }\n\n .ps-xxl-5 {\n padding-left: 3rem !important;\n }\n\n .text-xxl-start {\n text-align: left !important;\n }\n\n .text-xxl-end {\n text-align: right !important;\n }\n\n .text-xxl-center {\n text-align: center !important;\n }\n}\n@media (min-width: 1200px) {\n .fs-1 {\n font-size: 2.5rem !important;\n }\n\n .fs-2 {\n font-size: 2rem !important;\n }\n\n .fs-3 {\n font-size: 1.75rem !important;\n }\n\n .fs-4 {\n font-size: 1.5rem !important;\n }\n}\n@media print {\n .d-print-inline {\n display: inline !important;\n }\n\n .d-print-inline-block {\n display: inline-block !important;\n }\n\n .d-print-block {\n display: block !important;\n }\n\n .d-print-grid {\n display: grid !important;\n }\n\n .d-print-table {\n display: table !important;\n }\n\n .d-print-table-row {\n display: table-row !important;\n }\n\n .d-print-table-cell {\n display: table-cell !important;\n }\n\n .d-print-flex {\n display: flex !important;\n }\n\n .d-print-inline-flex {\n display: inline-flex !important;\n }\n\n .d-print-none {\n display: none !important;\n }\n}\n\n/*# sourceMappingURL=bootstrap.css.map */","// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n\n// Root\n//\n// Ability to the value of the root font sizes, affecting the value of `rem`.\n// null by default, thus nothing is generated.\n\n:root {\n @if $font-size-root != null {\n font-size: var(--#{$variable-prefix}root-font-size);\n }\n\n @if $enable-smooth-scroll {\n @media (prefers-reduced-motion: no-preference) {\n scroll-behavior: smooth;\n }\n }\n}\n\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Prevent adjustments of font size after orientation changes in iOS.\n// 4. Change the default tap highlight to be completely transparent in iOS.\n\n// scss-docs-start reboot-body-rules\nbody {\n margin: 0; // 1\n font-family: var(--#{$variable-prefix}body-font-family);\n @include font-size(var(--#{$variable-prefix}body-font-size));\n font-weight: var(--#{$variable-prefix}body-font-weight);\n line-height: var(--#{$variable-prefix}body-line-height);\n color: var(--#{$variable-prefix}body-color);\n text-align: var(--#{$variable-prefix}body-text-align);\n background-color: var(--#{$variable-prefix}body-bg); // 2\n -webkit-text-size-adjust: 100%; // 3\n -webkit-tap-highlight-color: rgba($black, 0); // 4\n}\n// scss-docs-end reboot-body-rules\n\n\n// Content grouping\n//\n// 1. Reset Firefox's gray color\n// 2. Set correct height and prevent the `size` attribute to make the `hr` look like an input field\n\nhr {\n margin: $hr-margin-y 0;\n color: $hr-color; // 1\n background-color: currentColor;\n border: 0;\n opacity: $hr-opacity;\n}\n\nhr:not([size]) {\n height: $hr-height; // 2\n}\n\n\n// Typography\n//\n// 1. Remove top margins from headings\n// By default, `

`-`

` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n\n%heading {\n margin-top: 0; // 1\n margin-bottom: $headings-margin-bottom;\n font-family: $headings-font-family;\n font-style: $headings-font-style;\n font-weight: $headings-font-weight;\n line-height: $headings-line-height;\n color: $headings-color;\n}\n\nh1 {\n @extend %heading;\n @include font-size($h1-font-size);\n}\n\nh2 {\n @extend %heading;\n @include font-size($h2-font-size);\n}\n\nh3 {\n @extend %heading;\n @include font-size($h3-font-size);\n}\n\nh4 {\n @extend %heading;\n @include font-size($h4-font-size);\n}\n\nh5 {\n @extend %heading;\n @include font-size($h5-font-size);\n}\n\nh6 {\n @extend %heading;\n @include font-size($h6-font-size);\n}\n\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\n\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n\n// Abbreviations\n//\n// 1. Duplicate behavior to the data-bs-* attribute for our tooltip plugin\n// 2. Add the correct text decoration in Chrome, Edge, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Prevent the text-decoration to be skipped.\n\nabbr[title],\nabbr[data-bs-original-title] { // 1\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n text-decoration-skip-ink: none; // 4\n}\n\n\n// Address\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\n\n// Lists\n\nol,\nul {\n padding-left: 2rem;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\n// 1. Undo browser default\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // 1\n}\n\n\n// Blockquote\n\nblockquote {\n margin: 0 0 1rem;\n}\n\n\n// Strong\n//\n// Add the correct font weight in Chrome, Edge, and Safari\n\nb,\nstrong {\n font-weight: $font-weight-bolder;\n}\n\n\n// Small\n//\n// Add the correct font size in all browsers\n\nsmall {\n @include font-size($small-font-size);\n}\n\n\n// Mark\n\nmark {\n padding: $mark-padding;\n background-color: $mark-bg;\n}\n\n\n// Sub and Sup\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n\nsub,\nsup {\n position: relative;\n @include font-size($sub-sup-font-size);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n// Links\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n\n &:hover {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([class]) {\n &,\n &:hover {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n// Code\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-code;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n direction: ltr #{\"/* rtl:ignore */\"};\n unicode-bidi: bidi-override;\n}\n\n// 1. Remove browser default top margin\n// 2. Reset browser default of `1em` to use `rem`s\n// 3. Don't allow content to break outside\n\npre {\n display: block;\n margin-top: 0; // 1\n margin-bottom: 1rem; // 2\n overflow: auto; // 3\n @include font-size($code-font-size);\n color: $pre-color;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n @include font-size(inherit);\n color: inherit;\n word-break: normal;\n }\n}\n\ncode {\n @include font-size($code-font-size);\n color: $code-color;\n word-wrap: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\nkbd {\n padding: $kbd-padding-y $kbd-padding-x;\n @include font-size($kbd-font-size);\n color: $kbd-color;\n background-color: $kbd-bg;\n @include border-radius($border-radius-sm);\n\n kbd {\n padding: 0;\n @include font-size(1em);\n font-weight: $nested-kbd-font-weight;\n }\n}\n\n\n// Figures\n//\n// Apply a consistent margin strategy (matches our type styles).\n\nfigure {\n margin: 0 0 1rem;\n}\n\n\n// Images and content\n\nimg,\nsvg {\n vertical-align: middle;\n}\n\n\n// Tables\n//\n// Prevent double borders\n\ntable {\n caption-side: bottom;\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: $table-cell-padding-y;\n padding-bottom: $table-cell-padding-y;\n color: $table-caption-color;\n text-align: left;\n}\n\n// 1. Removes font-weight bold by inheriting\n// 2. Matches default `` alignment by inheriting `text-align`.\n// 3. Fix alignment for Safari\n\nth {\n font-weight: $table-th-font-weight; // 1\n text-align: inherit; // 2\n text-align: -webkit-match-parent; // 3\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n}\n\n\n// Forms\n//\n// 1. Allow labels to use `margin` for spacing.\n\nlabel {\n display: inline-block; // 1\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n// See https://github.com/twbs/bootstrap/issues/24093\n\nbutton {\n // stylelint-disable-next-line property-disallowed-list\n border-radius: 0;\n}\n\n// Explicitly remove focus outline in Chromium when it shouldn't be\n// visible (e.g. as result of mouse click or touch tap). It already\n// should be doing this automatically, but seems to currently be\n// confused and applies its very visible two-tone outline anyway.\n\nbutton:focus:not(:focus-visible) {\n outline: 0;\n}\n\n// 1. Remove the margin in Firefox and Safari\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // 1\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\n// Remove the inheritance of text transform in Firefox\nbutton,\nselect {\n text-transform: none;\n}\n// Set the cursor for non-` +

Transportation Center

+ +
+ + + {/* Customers Admin */} + + + {(AuthService.canAddOrEditEmployees() || AuthService.canViewEmployees()) && } + { (AuthService.canAddOrEditCustomers() || AuthService.canViewCustomers()) && } + {(AuthService.canViewRoutes() || AuthService.canAddOrEditRoutes()) && goToRoutes()}>Routes} + {(AuthService.canAddOrEditVechiles() || AuthService.canViewVechiles()) && } + {(AuthService.canViewRoutes() || AuthService.canAddOrEditRoutes()) && } + {AuthService.canAccessLegacySystem() && goToMedical()}>Medical} + {AuthService.canAccessLegacySystem() && } + {(AuthService.canViewRoutes() || AuthService.canAddOrEditRoutes()) &&} + {(AuthService.canViewRoutes() || AuthService.canAddOrEditRoutes()) && } + + + +
+ +
+
+ +
+
+ + + ); +}; + +export default Admin; \ No newline at end of file diff --git a/client/src/components/admin/CustomerReport.js b/client/src/components/admin/CustomerReport.js new file mode 100644 index 0000000..5ab61fe --- /dev/null +++ b/client/src/components/admin/CustomerReport.js @@ -0,0 +1,725 @@ +import React, { useEffect, useState, Fragment, useRef } from "react"; +import { useNavigate } from 'react-router-dom'; +import { CustomerService, TransRoutesService, AuthService, ReportService } from "../../services"; +import DatePicker from "react-datepicker"; +import "react-datepicker/dist/react-datepicker.css"; +import { Modal, Button } from "react-bootstrap"; +import DateTimePicker from "react-datetime-picker"; +import { CSVLink } from "react-csv"; +import { CUSTOMER_TYPE, PERSONAL_ROUTE_STATUS, PERSONAL_ROUTE_STATUS_TEXT, REPORT_TYPE } from "../../shared"; +import { isEmpty, isEqual, xorWith } from 'lodash'; + +const CustomerReport = () => { + const navigate = useNavigate(); + const [customers, setCustomers] = useState([]); + const [datePicked, setDatePicked] = useState(new Date()); + const [routes, setRoutes] = useState([]); + const [sampleMap, setSampleMap] = useState(new Map()); + const [routeCustomerMap, setRouteCustomerMap] = useState(new Map()); + const [customerCheckInTime, setCustomerCheckInTime] = useState(undefined); + const [customerCheckOutTime, setCustomerCheckOutTime] = useState(undefined); + const [customerPickupTime, setCustomerPickupTime] = useState(undefined); + const [customerDropoffTime, setCustomerDropoffTime] = useState(undefined); + const [customerRouteReportStatus, setCustomerRouteReportStatus] = useState(''); + const [keyInEdit, setKeyInEdit] = useState(''); + const [statusFilter, setStatusFilter] = useState(''); + const [show, setShow] = useState(false); + const [nameFilter, setNameFilter] = useState(''); + const [callerFilter, setCallerFilter] = useState(''); + const [attendFilter, setAttendFilter] = useState(false); + const [existedReports, setExistedReports] = useState([]); + const [enableSave, setEnableSave] = useState(false); + const [csvData, setCsvData] = useState([]); + const csvInstance = useRef(null); + + const openForceEditModal = (key, customer_enter_center_time, customer_leave_center_time, customer_pickup_time, customer_dropoff_time, customer_route_report_status) => { + if (AuthService.canAddOrEditAttendance()) { + setShow(true); + setKeyInEdit(key); + setCustomerCheckInTime(customer_enter_center_time ? new Date(customer_enter_center_time) : undefined); + setCustomerCheckOutTime(customer_leave_center_time ? new Date(customer_leave_center_time) : undefined); + setCustomerPickupTime(customer_pickup_time ? new Date(customer_pickup_time) : undefined); + setCustomerDropoffTime(customer_dropoff_time ? new Date(customer_dropoff_time) : undefined); + setCustomerRouteReportStatus(customer_route_report_status); + // || getReportCustomerStatus({customer_enter_center_time, customer_leave_center_time, customer_pickup_time, customer_dropoff_time})); + } + } + + const closeModal = () => { + setCustomerCheckInTime(undefined); + setCustomerCheckOutTime(undefined); + setCustomerPickupTime(undefined); + setCustomerDropoffTime(undefined); + setKeyInEdit(''); + setShow(false); + } + + const saveReportInfo = () => { + const currentItem = routeCustomerMap.get(keyInEdit); + if (customerCheckInTime) { + currentItem.customer_enter_center_time = customerCheckInTime; + } + if (customerCheckOutTime) { + currentItem.customer_leave_center_time = customerCheckOutTime; + } + if (customerPickupTime) { + currentItem.customer_pickup_time = customerPickupTime; + } + if (customerDropoffTime) { + currentItem.customer_dropoff_time = customerDropoffTime; + } + if (customerRouteReportStatus) { + currentItem.customer_route_report_status = customerRouteReportStatus; + } + routeCustomerMap.set(keyInEdit, currentItem); + setRouteCustomerMap(new Map(routeCustomerMap)); + setShow(false); + setKeyInEdit('') + } + + const deleteItem = () => { + routeCustomerMap.delete(keyInEdit); + setKeyInEdit(''); + setShow(false); + }; + + const getRoutes = () => { + let customerMap = new Map(); + for (const customer of customers) { + customerMap.set(customer.id, { customer_name: `${customer.name} (${customer.name_cn})`, customer_type: `${customer.type}`, customer_caller: `${customer.caller}`, customer_seating: `${customer.seating}`, customer_vehicle: `${customer.vehicle_no}`}); + } + setSampleMap(new Map(customerMap)); + const date = new Date(datePicked); + const dateText = ((date.getMonth() > 8) ? (date.getMonth() + 1) : ('0' + (date.getMonth() + 1))) + '/' + ((date.getDate() > 9) ? date.getDate() : ('0' + date.getDate())) + '/' + date.getFullYear(); + TransRoutesService.getAll(dateText).then(data => { + setRoutes(data.data); + }) + ReportService.getReportsByDateAndType(dateText, REPORT_TYPE.ADMIN_CUSTOMER_REPORT).then(data => { + setExistedReports(data.data); + }) + } + + const isNotFullTime = (d1, d2) => { + return d1 && d2 && Math.abs(new Date(d1) - new Date(d2)) / 36e5 < 4; + } + + const getTimeString = (t) => { + return t && new Date(t).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) || ''; + } + + const diff_hours = (dt2, dt1) => { + let diff =(dt2?.getTime() - dt1?.getTime()) / 1000; + diff /= (60 * 60); + return Math.floor(Math.abs(diff)); + } + + const generateReportData = () => { + const date = new Date(datePicked); + const dateText = ((date.getMonth() > 8) ? (date.getMonth() + 1) : ('0' + (date.getMonth() + 1))) + '/' + ((date.getDate() > 9) ? date.getDate() : ('0' + date.getDate())) + '/' + date.getFullYear(); + const content = []; + ReportService.getReportsByDateAndType(dateText, REPORT_TYPE.ADMIN_CUSTOMER_REPORT).then(data => { + const latestExistedReports = data.data; + if (latestExistedReports && latestExistedReports.length > 0) { + for (const report of latestExistedReports[0]?.data) { + const currentData = routeCustomerMap.get(report.customer_id); + routeCustomerMap.set(report.customer_id, { + customer_name: currentData.customer_name, + customer_type: currentData.customer_type, + customer_caller: currentData.customer_caller, + customer_route_report_status: currentData.customer_route_report_status || report.customer_route_report_status, + customer_pickup_time: currentData.customer_pickup_time || report.customer_pickup_time, + customer_enter_center_time: currentData.customer_enter_center_time || report.customer_enter_center_time, + customer_leave_center_time: currentData.customer_leave_center_time || report.customer_leave_center_time, + customer_dropoff_time: currentData.customer_dropoff_time || report.customer_dropoff_time, + customer_vehicle: currentData.customer_vehicle || report.customer_vehicle, + customer_seating: currentData.customer_seating || report.customer_seating + }) + + } + } + const head = ['No.', 'Name', 'Customer Route Status', 'Pick-Up', 'Arrival', 'Departure', 'Drop-Off', 'Full Time Attendence?', 'Hours Stayed', 'Customer Type', 'Caller', 'Seating', 'Vehicle Number']; + const chineseHead=['序号', '姓名', '客户路线状态', '接到时间', '抵达中心', '离开中心', '送达时间', '是否全勤', '出勤时间(小时)', '用户类别', '联系人', '座位', '车号']; + const customersList = Array.from(routeCustomerMap.values()).filter(item => [CUSTOMER_TYPE.MEMBER, CUSTOMER_TYPE.SELF_PAY].includes(item.customer_type)).filter( + (item) => item?.customer_name?.toLowerCase()?.includes(nameFilter?.toLowerCase()) + // && ( + // !attendFilter || + // attendFilter && (item.customer_enter_center_time || item.customer_leave_center_time) + // ) + ); + for (let i=0; i { + // const head = ['No.', 'Name', 'Customer Route Status', 'Pick-Up', 'Arrival', 'Departure', 'Drop-Off', 'Full Time Attendence?', 'Hours Stayed', 'Customer Type', 'Caller', 'Seating']; + // const chineseHead=['序号', '姓名', '客户路线状态', '接到时间', '抵达中心', '离开中心', '送达时间', '是否全勤', '出勤时间(小时)', '用户类别', '联系人', '座位']; + // const customersList = Array.from(routeCustomerMap.values()).filter(item => item.customer_type === CUSTOMER_TYPE.MEMBER).filter( + // (item) => item?.customer_name?.includes(nameFilter) + // // && ( + // // !attendFilter || + // // attendFilter && (item.customer_enter_center_time || item.customer_leave_center_time) + // // ) + // ).sort((a,b) => a.customer_name > b.customer_name ? 1: -1); + // const content = [] + // for (let i=0; i { + if (customer.customer_dropoff_time) { + return PERSONAL_ROUTE_STATUS_TEXT.droppedOff.text; + } else { + if (customer.customer_leave_center_time) { + return PERSONAL_ROUTE_STATUS_TEXT.leftCenter.text; + } else { + if (customer.customer_enter_center_time) { + return PERSONAL_ROUTE_STATUS_TEXT.inCenter.text; + } else { + if (customer.customer_pickup_time) { + return PERSONAL_ROUTE_STATUS_TEXT.picked.text; + } else { + return '' + } + } + } + } + } + + const generateCustomerReport = () => { + const date = new Date(datePicked); + const dateText = ((date.getMonth() > 8) ? (date.getMonth() + 1) : ('0' + (date.getMonth() + 1))) + '/' + ((date.getDate() > 9) ? date.getDate() : ('0' + date.getDate())) + '/' + date.getFullYear(); + const content = []; + ReportService.getReportsByDateAndType(dateText, REPORT_TYPE.ADMIN_CUSTOMER_REPORT).then(data => { + const latestExistedReports = data.data; + if (latestExistedReports && latestExistedReports.length > 0) { + for (const report of latestExistedReports[0]?.data) { + const currentData = routeCustomerMap.get(report.customer_id); + routeCustomerMap.set(report.customer_id, { + customer_name: currentData.customer_name, + customer_type: currentData.customer_type, + customer_caller: currentData.customer_caller, + customer_route_report_status: currentData.customer_route_report_status || report.customer_route_report_status, + customer_pickup_time: currentData.customer_pickup_time || report.customer_pickup_time, + customer_enter_center_time: currentData.customer_enter_center_time || report.customer_enter_center_time, + customer_leave_center_time: currentData.customer_leave_center_time || report.customer_leave_center_time, + customer_dropoff_time: currentData.customer_dropoff_time || report.customer_dropoff_time, + customer_seating: currentData.customer_seating, + customer_vehicle: currentData.customer_vehicle + }) + + } + } + setRouteCustomerMap(new Map(routeCustomerMap)); + const customersList = Array.from(routeCustomerMap.entries()).filter(([customer_id, item]) => [CUSTOMER_TYPE.MEMBER, CUSTOMER_TYPE.SELF_PAY].includes(item.customer_type)) + .filter( + ([customer_id, item]) => item?.customer_name?.toLowerCase()?.includes(nameFilter?.toLowerCase())); + // // && ( + // !attendFilter || + // attendFilter && (item.customer_enter_center_time || item.customer_leave_center_time) + // ) + for (let i=0; i 0) { + ReportService.updateReport(existedReports[0]?.id, { + date: dateText, + type: REPORT_TYPE.ADMIN_CUSTOMER_REPORT, + head: ['No.', 'Name', 'Customer Route Status', 'Pick-Up', 'Arrival', 'Departure', 'Drop-Off', 'Full Time Attendence?', 'Hours Stayed', 'Customer Type', 'Caller', 'Seating', 'Vehicle Number'], + chinese_head: ['序号', '姓名', '客户路线状态', '接到时间', '抵达中心', '离开中心', '送达时间', '是否全勤', '出勤时间(小时)', '用户类别', '联系人', '座位', '车号'], + data: content.sort((a,b) => a.customer_name > b.customer_name ? 1: -1) + }).then(() => { + window.alert('The report is saved to Database. To get a PDF version, please run your PYTHON EXE Script.') + }) + } else { + ReportService.createReport({ + date: dateText, + type: REPORT_TYPE.ADMIN_CUSTOMER_REPORT, + head: ['No.', 'Name', 'Customer Route Status', 'Pick-Up', 'Arrival', 'Departure', 'Drop-Off', 'Full Time Attendence?', 'Hours Stayed', 'Customer Type', 'Caller', 'Seating', 'Vehicle Number'], + chinese_head: ['序号', '姓名', '客户路线状态', '接到时间', '抵达中心', '离开中心', '送达时间', '是否全勤', '出勤时间(小时)', '用户类别', '联系人', '座位', '车号'], + data: content.sort((a,b) => a.customer_name > b.customer_name ? 1: -1) + }).then(() => { + window.alert('The report is saved to Database. To get a PDF version, please run your PYTHON EXE Script.') + }) + } + }) + + } + + + const getRoutesCustomerMap = () => { + const routeMap = new Map(); + for (const route of routes) { + for (const customer of route.route_customer_list) { + let current = routeMap.has(customer.customer_id)?routeMap.get(customer.customer_id) : {customer_name: customer.customer_name}; + + if (customer.customer_enter_center_time && customer.customer_enter_center_time !== '' ) { + current.customer_enter_center_time = customer.customer_enter_center_time; + } + if (customer.customer_leave_center_time && customer.customer_leave_center_time !=='' ) { + current.customer_leave_center_time = customer.customer_leave_center_time; + } + if (customer.customer_pickup_time && customer.customer_pickup_time !== '' ) { + current.customer_pickup_time = customer.customer_pickup_time; + } + if (customer.customer_dropoff_time && customer.customer_dropoff_time !== '' ) { + current.customer_dropoff_time = customer.customer_dropoff_time; + } + if (customer.customer_type && customer.customer_type !== '') { + current.customer_type = customer.customer_type; + } + if (customer.customer_route_status && customer.customer_route_status !== '') { + current.customer_route_report_status = PERSONAL_ROUTE_STATUS_TEXT[customer.customer_route_status]?.text; + } + routeMap.set(customer.customer_id, Object.assign({}, sampleMap.get(customer.customer_id), current)); + } + } + setRouteCustomerMap(new Map([...sampleMap, ...routeMap])); + } + + const getRoutesCustomerMapFromExistedReport = () => { + const routeMap = new Map(); + if (existedReports && existedReports.length > 0) { + for (const report of existedReports[0]?.data) { + + let current = routeMap.has(report.customer_id)?routeMap.get(report.customer_id) : {customer_name: report.customer_name}; + if (report.customer_enter_center_time && report.customer_enter_center_time !== '' ) { + current.customer_enter_center_time = report.customer_enter_center_time; + } + if (report.customer_leave_center_time && report.customer_leave_center_time !=='' ) { + current.customer_leave_center_time = report.customer_leave_center_time; + } + if (report.customer_pickup_time && report.customer_pickup_time !== '' ) { + current.customer_pickup_time = report.customer_pickup_time; + } + if (report.customer_dropoff_time && report.customer_dropoff_time !== '' ) { + current.customer_dropoff_time = report.customer_dropoff_time; + } + if (report.customer_type && report.customer_type !== '') { + current.customer_type = report.customer_type; + } + if (report.customer_route_report_status && report.customer_route_report_status !== '') { + current.customer_route_report_status = report.customer_route_report_status; + } + routeMap.set(report.customer_id, Object.assign({}, sampleMap.get(report.customer_id), current)); + } + const inboundRoutes = routes.filter(route => route.type === 'inbound'); + const outboundRoutes = routes.filter(route => route.type === 'outbound'); + for (const route of inboundRoutes) { + for (const customer of route.route_customer_list) { + let current = routeMap.has(customer.customer_id)?routeMap.get(customer.customer_id) : {customer_name: customer.customer_name}; + + if (customer.customer_enter_center_time && customer.customer_enter_center_time !== '' ) { + current.customer_enter_center_time = customer.customer_enter_center_time; + } + if (customer.customer_leave_center_time && customer.customer_leave_center_time !=='' ) { + current.customer_leave_center_time = customer.customer_leave_center_time; + } + if (customer.customer_pickup_time && customer.customer_pickup_time !== '' ) { + current.customer_pickup_time = customer.customer_pickup_time; + } + if (customer.customer_dropoff_time && customer.customer_dropoff_time !== '' ) { + current.customer_dropoff_time = customer.customer_dropoff_time; + } + if (customer.customer_type && customer.customer_type !== '') { + current.customer_type = customer.customer_type; + } + if (customer.customer_route_status && customer.customer_route_status !== '' && customer.customer_route_status !== PERSONAL_ROUTE_STATUS.NO_STATUS) { + current.customer_route_report_status = PERSONAL_ROUTE_STATUS_TEXT[customer.customer_route_status]?.text; + } + routeMap.set(customer.customer_id, Object.assign({}, sampleMap.get(customer.customer_id), current)); + } + } + for (const route of outboundRoutes) { + for (const customer of route.route_customer_list) { + let current = routeMap.has(customer.customer_id)?routeMap.get(customer.customer_id) : {customer_name: customer.customer_name}; + + if (customer.customer_enter_center_time && customer.customer_enter_center_time !== '' ) { + current.customer_enter_center_time = customer.customer_enter_center_time; + } + if (customer.customer_leave_center_time && customer.customer_leave_center_time !=='' ) { + current.customer_leave_center_time = customer.customer_leave_center_time; + } + if (customer.customer_pickup_time && customer.customer_pickup_time !== '' ) { + current.customer_pickup_time = customer.customer_pickup_time; + } + if (customer.customer_dropoff_time && customer.customer_dropoff_time !== '' ) { + current.customer_dropoff_time = customer.customer_dropoff_time; + } + if (customer.customer_type && customer.customer_type !== '') { + current.customer_type = customer.customer_type; + } + if (customer.customer_route_status && customer.customer_route_status !== '' && customer.customer_route_status !== PERSONAL_ROUTE_STATUS.NO_STATUS) { + current.customer_route_report_status = PERSONAL_ROUTE_STATUS_TEXT[customer.customer_route_status]?.text; + } + routeMap.set(customer.customer_id, Object.assign({}, sampleMap.get(customer.customer_id), current)); + } + } + setRouteCustomerMap(new Map([...sampleMap, ...routeMap])); + } + } + + const syncLatestRouteStatus = () => { + const routeMap = new Map(routeCustomerMap); + const date = new Date(datePicked); + const dateText = ((date.getMonth() > 8) ? (date.getMonth() + 1) : ('0' + (date.getMonth() + 1))) + '/' + ((date.getDate() > 9) ? date.getDate() : ('0' + date.getDate())) + '/' + date.getFullYear(); + TransRoutesService.getAll(dateText).then(data => { + const routes = data.data; + const inboundRoutes = routes.filter(route => route.type === 'inbound'); + const outboundRoutes = routes.filter(route => route.type === 'outbound'); + for (const route of inboundRoutes) { + for (const customer of route.route_customer_list) { + if (customer.customer_route_status && customer.customer_route_status !== '' && customer.customer_route_status!== PERSONAL_ROUTE_STATUS.NO_STATUS) { + let current = routeMap.has(customer.customer_id)?routeMap.get(customer.customer_id) : {customer_name: customer.customer_name}; + + if (customer.customer_enter_center_time && customer.customer_enter_center_time !== '' ) { + current.customer_enter_center_time = customer.customer_enter_center_time; + } + if (customer.customer_leave_center_time && customer.customer_leave_center_time !=='' ) { + current.customer_leave_center_time = customer.customer_leave_center_time; + } + if (customer.customer_pickup_time && customer.customer_pickup_time !== '' ) { + current.customer_pickup_time = customer.customer_pickup_time; + } + if (customer.customer_dropoff_time && customer.customer_dropoff_time !== '' ) { + current.customer_dropoff_time = customer.customer_dropoff_time; + } + if (customer.customer_type && customer.customer_type !== '') { + current.customer_type = customer.customer_type; + } + if (customer.customer_route_status && customer.customer_route_status !== '' && customer.customer_route_status !== PERSONAL_ROUTE_STATUS.NO_STATUS) { + current.customer_route_report_status = PERSONAL_ROUTE_STATUS_TEXT[customer.customer_route_status]?.text; + } + routeMap.set(customer.customer_id, Object.assign({}, sampleMap.get(customer.customer_id), current)); + } + + } + } + for (const route of outboundRoutes) { + for (const customer of route.route_customer_list) { + if (customer.customer_route_status && customer.customer_route_status !== '' && customer.customer_route_status!== PERSONAL_ROUTE_STATUS.NO_STATUS) { + let current = routeMap.has(customer.customer_id)?routeMap.get(customer.customer_id) : {customer_name: customer.customer_name}; + + if (customer.customer_enter_center_time && customer.customer_enter_center_time !== '' ) { + current.customer_enter_center_time = customer.customer_enter_center_time; + } + if (customer.customer_leave_center_time && customer.customer_leave_center_time !=='' ) { + current.customer_leave_center_time = customer.customer_leave_center_time; + } + if (customer.customer_pickup_time && customer.customer_pickup_time !== '' ) { + current.customer_pickup_time = customer.customer_pickup_time; + } + if (customer.customer_dropoff_time && customer.customer_dropoff_time !== '' ) { + current.customer_dropoff_time = customer.customer_dropoff_time; + } + if (customer.customer_type && customer.customer_type !== '') { + current.customer_type = customer.customer_type; + } + if (customer.customer_route_status && customer.customer_route_status !== '' && customer.customer_route_status !== PERSONAL_ROUTE_STATUS.NO_STATUS) { + current.customer_route_report_status = PERSONAL_ROUTE_STATUS_TEXT[customer.customer_route_status]?.text; + } + routeMap.set(customer.customer_id, Object.assign({}, sampleMap.get(customer.customer_id), current)); + } + + } + } + setRouteCustomerMap(new Map([...sampleMap, ...routeMap])); + }) + }; + + useEffect(() => { + CustomerService.getAllActiveCustomers().then((data) => setCustomers(data.data)); + }, []); + + useEffect(() => { + let customerMap = new Map(); + for (const customer of customers) { + customerMap.set(customer.id, { customer_name: `${customer.name} (${customer.name_cn})`, customer_caller: customer.caller, customer_vehicle: customer.vehicle_no, customer_seating: customer.seating}); + } + setSampleMap(new Map(customerMap)); + }, [customers]); + + useEffect(() => { + if (existedReports && existedReports.length > 0) { + getRoutesCustomerMapFromExistedReport(); + } else { + getRoutesCustomerMap(); + } + + }, [routes, existedReports]); + + useEffect(() => { + if (csvData && csvInstance && csvInstance.current && csvInstance.current.link) { + setTimeout(() => { + csvInstance.current.link.click(); + setCsvData([]); + }); + } + }, [csvData]); + + useEffect(() => { + const content = []; + const customersList = Array.from(routeCustomerMap.entries()).filter(([customer_id, item]) =>[CUSTOMER_TYPE.MEMBER, CUSTOMER_TYPE.SELF_PAY].includes( item.customer_type)) + .filter( + ([customer_id, item]) => item?.customer_name?.toLowerCase()?.includes(nameFilter?.toLowerCase())); + for (let i=0; i 0) { + const isArrayEqual = (x, y) => isEmpty(xorWith(x, y, isEqual)); + setEnableSave(!isArrayEqual(content.sort((a, b) => a.customer_name > b.customer_name ? 1: -1), existedReports[0]?.data.sort((a, b) => a.customer_name > b.customer_name ? 1: -1))); + } else { + setEnableSave(true); + } + } + }, [routeCustomerMap]); + + return ( + <> + { + (AuthService.canAddOrEditAttendance() || AuthService.canViewAttendance() ) && ( + <> +
+
+ Select Date for Customer Report: +
+
+ setDatePicked(v)} /> +
+
+ +
+
+
+
+
+ Filter by User Name: setNameFilter(e.currentTarget.value)}/> +
+
+ Filter by Caller: setCallerFilter(e.currentTarget.value)}/> +
+
+ Filter by Customer Route Status: +
+
+ Include All Customer Attending Today: setAttendFilter(!attendFilter)}/> +
+
+ {/* + Generate Customer Reports + */} + + +
{ + generateReportData(); + }} + > + + +
+
+ + + {csvData.length > 0 ? + + : undefined} + + +
Report Details Table
+ + + + + + + + + + + + + + + + + + + { + Array.from(routeCustomerMap.entries()).filter( + (item) => item[1]?.customer_name?.toLowerCase()?.includes(nameFilter?.toLowerCase()) && ( + !attendFilter || + attendFilter && (item[1].customer_enter_center_time || item[1].customer_leave_center_time) + )) + .filter(item => item[1]?.customer_caller?.toLowerCase()?.includes(callerFilter?.toLowerCase())) + .filter(item => [CUSTOMER_TYPE.MEMBER, CUSTOMER_TYPE.SELF_PAY].includes(item[1].customer_type)) + .filter(item => statusFilter ? (statusFilter !== 'No Status' ? item[1].customer_route_report_status === statusFilter : (!item[1].customer_route_report_status || item[1].customer_route_report_status === statusFilter)):item) + .sort((a, b) => a[1].customer_name > b[1].customer_name ? 1 : -1) + .map(([key, {customer_name, customer_pickup_time, customer_dropoff_time, customer_enter_center_time, customer_leave_center_time, customer_type, customer_caller, customer_route_report_status, customer_vehicle, customer_seating}], index) => { + return ( openForceEditModal(key, customer_enter_center_time, customer_leave_center_time, customer_pickup_time, customer_dropoff_time, customer_route_report_status)}> + + + + + + + + + + + + + ) + }) + } + +
No.NameCustomer Route StatusPick Up TimeEnter Center TimeLeave Center TimeDrop Off TimeHours StayedCutomer TypeCallerSeatingVehicle Number
{index+1}{customer_name}{(customer_route_report_status)}{customer_pickup_time && new Date(customer_pickup_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}{customer_enter_center_time && new Date(customer_enter_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}{customer_leave_center_time && new Date(customer_leave_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}{customer_dropoff_time && new Date(customer_dropoff_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}{customer_enter_center_time && customer_leave_center_time && diff_hours(new Date(customer_enter_center_time), new Date(customer_leave_center_time))}{customer_type}{customer_caller}{customer_seating}{customer_vehicle}
+
+
+ closeModal()}> + + Special Change Request Client + + + <> +
+ Special Checkin: {if (!customerCheckInTime || customerCheckInTime.length === 0) { setCustomerCheckInTime(new Date())}}} format={'MM/dd/y HH:mm'} value={customerCheckInTime} disableClock={true} onChange={setCustomerCheckInTime} /> +
+
+
+ Special Checkout: {if (!customerCheckOutTime || customerCheckOutTime.length === 0) { setCustomerCheckOutTime(new Date())}}} format={'MM/dd/y HH:mm'} value={customerCheckOutTime} disableClock={true} onChange={setCustomerCheckOutTime} /> +
+
+
+ Pickup Time: {if (!customerPickupTime || customerPickupTime.length === 0) { setCustomerPickupTime(new Date())}}} format={'MM/dd/y HH:mm'} value={customerPickupTime} disableClock={true} onChange={setCustomerPickupTime} /> +
+
+
+ Dropoff Time: {if (!customerDropoffTime || customerDropoffTime.length === 0) { setCustomerDropoffTime(new Date())}}} format={'MM/dd/y HH:mm'} value={customerDropoffTime} disableClock={true} onChange={setCustomerDropoffTime} /> +
+
+
+ Special Change Customer Route status: + +
+ +
+ + + + + +
+ )} + + + ); +}; + +export default CustomerReport; \ No newline at end of file diff --git a/client/src/components/center-phone/CenterPhoneList.js b/client/src/components/center-phone/CenterPhoneList.js new file mode 100644 index 0000000..3935c09 --- /dev/null +++ b/client/src/components/center-phone/CenterPhoneList.js @@ -0,0 +1,78 @@ +import React, {useState, useEffect} from "react"; +import { useDispatch } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import { AuthService, CenterPhoneService } from "../../services"; + +const CenterPhoneList = () => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const [phones, setPhones] = useState([]); + const [keyword, setKeyword] = useState(''); + const params = new URLSearchParams(window.location.search); + + useEffect(() => { + if (!AuthService.canAddOrEditRoutes() && !AuthService.canViewRoutes()&&!AuthService.canAccessLegacySystem()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + CenterPhoneService.getAll().then((data) => + setPhones(data.data) + ); + }, []); + + const redirectToAdmin = () => { + if (params.get('from') === 'medical') { + navigate(`/medical/`); + } else { + navigate(`/admin/customer-report`) + } + + } + + const goToEdit = (id) => { + navigate(`/center-phones/edit/${id}`) + } + + + return ( + <> +
+
+
All Phones
+
+
+
+
+
Keyword: setKeyword(e.currentTarget.value)}/>
+ + + + + + + + + + + + { + phones && phones.filter((item)=> item?.phone_number.includes(keyword) || item?.phone_title.toLowerCase().includes(keyword.toLowerCase())).map(phone => + + + + + ) + } + +
Phone TitlePhone NumberActivated
{phone?.phone_title}{phone?.phone_number}{phone?.activated ? 'Yes': 'No'} + +
+ +
+
+ + ) +}; + +export default CenterPhoneList; \ No newline at end of file diff --git a/client/src/components/center-phone/CreateCenterPhone.js b/client/src/components/center-phone/CreateCenterPhone.js new file mode 100644 index 0000000..c782b5d --- /dev/null +++ b/client/src/components/center-phone/CreateCenterPhone.js @@ -0,0 +1,65 @@ +import React, {useEffect, useState} from "react"; +import { useNavigate } from "react-router-dom"; +import { AuthService, CenterPhoneService } from "../../services"; + +const CreateCenterPhone = () => { + const navigate = useNavigate(); + const [phoneTitle, setPhoneTitle] = useState(''); + const [phoneNumber, setPhoneNumber] = useState(''); + const params = new URLSearchParams(window.location.search); + + useEffect(() => { + if (!AuthService.canAddOrEditRoutes() && !AuthService.canViewRoutes()&&!AuthService.canAccessLegacySystem()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an Dispatcher or admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + }, []); + + + const redirectTo = () => { + if (params.get('from') === 'medical') { + navigate(`/medical/`); + } else { + navigate(`/admin/customer-report`) + } + + } + + const savePhone = () => { + const data = { + phone_title: phoneTitle, + phone_number: phoneNumber + }; + CenterPhoneService.createNewCenterPhone(data).then(() => { + navigate(`/center-phones/list`) + }); + } + + + return ( + <> +
+
+
Create New Center Phone Item
+
+
+
+
+
Phone Title(*):
setPhoneTitle(e.target.value)}/> +
+
+
Phone Number(*):
setPhoneNumber(e.target.value)}/> +
+
+
+
+ + +
+
+ + ); +}; + +export default CreateCenterPhone; \ No newline at end of file diff --git a/client/src/components/center-phone/UpdateCenterPhone.js b/client/src/components/center-phone/UpdateCenterPhone.js new file mode 100644 index 0000000..f47f431 --- /dev/null +++ b/client/src/components/center-phone/UpdateCenterPhone.js @@ -0,0 +1,74 @@ +import React, {useEffect, useState} from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { AuthService, CenterPhoneService } from "../../services"; + +const UpdateCenterPhone = () => { + const navigate = useNavigate(); + const [phoneTitle, setPhoneTitle] = useState(''); + const [phoneNumber, setPhoneNumber] = useState(''); + const [activated, setActivated] = useState(false); + const [currentPhone, setCurrentPhone] = useState(undefined); + const urlParams = useParams(); + + useEffect(() => { + if (!AuthService.canAddOrEditRoutes() && !AuthService.canViewRoutes()&&!AuthService.canAccessLegacySystem()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an Dispatcher or admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + if (!currentPhone) { + CenterPhoneService.getCenterPhone(urlParams.id).then(data => setCurrentPhone(data.data)) + } + }, []); + + useEffect(() => { + if (currentPhone) { + setPhoneNumber(currentPhone.phone_number); + setPhoneTitle(currentPhone.phone_title); + setActivated(currentPhone.activated); + } + + }, [currentPhone]) + + const redirectTo = () => { + navigate('/center-phones/list') + } + + const savePhone = () => { + const data = { + phone_title: phoneTitle, + phone_number: phoneNumber, + activated: activated + }; + CenterPhoneService.updateCenterPhone(urlParams.id, data).then(() => redirectTo()) + } + + return ( + <> +
+
+
Update Phone
+
+
+
+
+
Phone Title(*):
setPhoneTitle(e.target.value)}/> +
+
+
Phone Number(*):
setPhoneNumber(e.target.value)}/> +
+
+
Activated:
setActivated(!activated)}/> +
+
+
+
+ + +
+
+ + ); +}; + +export default UpdateCenterPhone; \ No newline at end of file diff --git a/client/src/components/customers/CreateCustomer.js b/client/src/components/customers/CreateCustomer.js new file mode 100644 index 0000000..d8eeb54 --- /dev/null +++ b/client/src/components/customers/CreateCustomer.js @@ -0,0 +1,376 @@ +import React, {useState, useEffect} from "react"; +import { useDispatch } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import { customerSlice } from "../../store"; +import { AuthService, CustomerService, ResourceService } from "../../services"; +import Select from 'react-select'; +import { CUSTOMER_TYPE, PICKUP_STATUS, PICKUP_STATUS_TEXT , CUSTOMER_TYPE_TEXT} from "../../shared"; + +const CreateCustomer = () => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const { createCustomer } = customerSlice.actions; + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [firstname, setFirstname] = useState(''); + const [lastname, setLastname] = useState(''); + const [nameCN, setNameCN] = useState(''); + const [birthDate, setBirthDate] = useState(''); + const [email, setEmail] = useState(''); + const [mobilePhone, setMobilePhone] = useState(''); + const [phone, setPhone] = useState(''); + const [homePhone, setHomePhone] = useState(''); + const [tableId, setTableId] = useState(''); + const [language, setLanguage] = useState(''); + const [address1, setAddress1] = useState(''); + const [address2, setAddress2] = useState(''); + const [address3, setAddress3] = useState(''); + const [address4, setAddress4] = useState(''); + const [address5, setAddress5] = useState(''); + const [customerType, setCustomerType] = useState(''); + const [selectedFile, setSelectedFile] = useState(); + const [careProvider, setCareProvider] = useState(''); + const [medicareNumber, setMedicareNumber] = useState(''); + const [medicaidNumber, setMedicaidNumber] = useState(''); + const [pharmacy, setPharmacy] = useState(''); + const [pharmacyId, setPharmacyId] = useState(''); + const [pickupStatus, setPickupStatus] = useState(''); + const [specialNeeds, setSpecialNeeds] = useState(''); + const [emergencyContact, setEmergencyContact] = useState(''); + const [admissionDate, setAdmissionDate] = useState(''); + const [vehicleNo, setVehicleNo] = useState(''); + const [note, setNote] = useState(''); + const [pin, setPin] = useState(''); + const [seating, setSeating] = useState(''); + const [caller, setCaller] = useState(''); + const [dischargeDate, setDischargeDate] = useState(''); + const [placement, setPlacement] = useState(''); + const [nickname, setNickname] = useState(''); + const [groups, setGroups] = useState(''); + const [tags, setTags] = useState(''); + const [roles, setRoles] = useState(''); + const [apartment, setApartment] = useState(''); + const [resources, setResources] = useState([]); + const [privateNote, setPrivateNote] = useState(''); + const [disability, setDisability ] = useState(false); + const [gender, setGender] = useState(''); + const [weight, setWeight] = useState(''); + const [heightFeet, setHeightFeet] = useState(''); + const [heightInch, setHeightInch] = useState(''); + const [textMsgEnabled, setTextMsgEnabled] = useState(false); + const params = new URLSearchParams(window.location.search); + const redirectTo = () => { + navigate('/admin'); + } + + useEffect(() => { + if (!AuthService.canAddOrEditCustomers()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + ResourceService.getAll('pharmacy').then(data => { + setResources(data.data); + }) + }, []) + + const saveCustomer = () => { + const data = { + username, + name: customerType === 'discharged' ? `${lastname},${firstname} (discharged)` : `${lastname},${firstname}`, + name_cn: nameCN, + email, + password, + mobile_phone: mobilePhone, + phone, + table_id: tableId, + home_phone: homePhone, + type: customerType, + language, + status: 'active', + address1, + address2, + address3, + address4, + address5, + firstname, + lastname, + birth_date: birthDate, + care_provider: careProvider, + medicare_number: medicareNumber, + medicaid_number: medicaidNumber, + pharmacy: pharmacy?.label || '', + pharmacy_id: pharmacyId, + pickup_status: pickupStatus, + special_needs: specialNeeds, + emergency_contact: emergencyContact, + admission_date: admissionDate, + vehicle_no: vehicleNo, + discharge_date: dischargeDate, + create_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name || '', + edit_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name || '', + note, + pin, + seating, + caller, + placement, + nickname, + groups: groups.replace(' ', '').split(','), + tags: tags.replace(' ', '').split(','), + roles: roles.replace(' ', '').split(','), + apartment, + private_note: privateNote, + disability: disability === 'true', + weight, + gender, + height: `${heightFeet} ft ${heightInch} in`, + text_msg_enabled: textMsgEnabled === 'true' || false + }; + const dataForLegacy = { + username, + parentId: '5eee3552b02fac3d4acfd5ea', + name: customerType === 'discharged' ? `${lastname},${firstname} (discharged)` : `${lastname},${firstname}`, + name_cn: `${lastname},${firstname} ${nameCN}`, + email, + password, + mobile_phone: mobilePhone, + phone, + table_id: tableId, + home_phone: homePhone, + type: customerType, + language, + status: 'active', + address: address1, + address2, + firstname, + lastname, + birth_date: birthDate, + care_provider: careProvider, + medicare_number: medicareNumber, + medicaid_number: medicaidNumber, + pharmacy: pharmacy?.label || '', + pharmacy_id: pharmacyId, + emergency_contact: emergencyContact, + admission_date: admissionDate, + vehicle_no: vehicleNo, + discharge_date: dischargeDate, + create_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name || '', + edit_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name || '', + note, + pin, + seating, + caller, + placement, + nickname, + groups: groups.replace(' ', '').split(','), + tags: tags.replace(' ', '').split(','), + roles: roles.replace(' ', '').split(','), + private_note: privateNote + }; + const formData = new FormData(); + formData.append("file", selectedFile); + let payload = { data, dataForLegacy }; + if (selectedFile) { + payload = Object.assign({}, payload, {avatar: formData}) + } + dispatch(createCustomer(payload)); + redirectTo(); + }; + + const onPharmacyChange = (selectedPharmacy) => { + setPharmacy(selectedPharmacy); + setPharmacyId(selectedPharmacy?.value); + } + + return ( + <> +
+
+
Create New Customer
+
+
+
+
+
First Name:(*)
setFirstname(e.target.value)}/> +
+
+
Last Name:(*)
setLastname(e.target.value)}/> +
+
+
Preferred Name (中文姓名):
setNameCN(e.target.value)}/> +
+
+
Nick Name:
setNickname(e.target.value)}/> +
+
+
Email:(*)
setEmail(e.target.value)}/> +
+
+
Care Provider:
setCareProvider(e.target.value)}/> +
+
+
Emergency Contact:
setEmergencyContact(e.target.value)}/> +
+
+
Medicare Number:
setMedicareNumber(e.target.value)}/> +
+
+
Medicaid Number:
setMedicaidNumber(e.target.value)}/> +
+
+ {/*
Pharmacy:
setPharmacy(e.target.value)}/> */} +
Pharmacy
+ +
+
+
Pharmacy ID:
setPharmacyId(e.target.value)}/> +
+
+
Address 1:
setAddress1(e.target.value)}/> +
+
+
Address 2:
setAddress2(e.target.value)}/> +
+
+
Address 3:
setAddress3(e.target.value)}/> +
+
+
Address 4:
setAddress4(e.target.value)}/> +
+
+
Address 5:
setAddress5(e.target.value)}/> +
+
+
Apartment:
setApartment(e.target.value)}/> +
+
+
Table Id:
setTableId(e.target.value)}/> +
+
+
Customer Type:
+
+
+
Pickup Status:
+
+
+
Upload Avatar (Image size should be less than 500 KB):
+ setSelectedFile(e.target.files[0])} + className="form-control-file border" + /> + +
+
+
Birth Date (Type in as: MM/DD/YYYY):
setBirthDate(e.target.value)}/> +
+
+
Phone :
setPhone(e.target.value)}/> +
+
+
Mobile Phone:
setMobilePhone(e.target.value)}/> +
+
+
Home Phone:
setHomePhone(e.target.value)}/> +
+
+
Special Needs:
setSpecialNeeds(e.target.value)}/> +
+
+
Language(Please use ',' between each language):
setLanguage(e.target.value)}/> +
+
+
Username (not required):
setUsername(e.target.value)}/> +
+
+
Password (not required):
setPassword(e.target.value)}/> +
+
+
Note:
setNote(e.target.value)}/> +
+ +
+
Admission Date(Type in as 'MM/DD/YYYY'):
setAdmissionDate(e.target.value)}/> +
+
+
Discharge Date(Type in as 'MM/DD/YYYY'):
setDischargeDate(e.target.value)}/> +
+
+
Pin:
setPin(e.target.value)}/> +
+
+
Seating:
setSeating(e.target.value)}/> +
+
+
Vehicle No:
setVehicleNo(e.target.value)}/> +
+
+
Caller:
setCaller(e.target.value)}/> +
+
+
Placement:
setPlacement(e.target.value)}/> +
+
+
Groups(Please use ',' between each group):
setGroups(e.target.value)}/> +
+
+
Tags(Please use ',' between each tags):
setTags(e.target.value)}/> +
+
+
Roles(Please use ',' between each roles):
setRoles(e.target.value)}/> +
+
+
Private Note:
setPrivateNote(e.target.value)}/> +
+
+
Disability:
+
+
+
Height:
+ setHeightFeet(e.target.value)}/> Ft + setHeightInch(e.target.value)}/> In +
+
+
Weight:
setWeight(e.target.value)}/> lb +
+
+
Gender:
+
+
+
Enable Text Message:
+
+
+
+
+ + +
+
+ + ); +}; + +export default CreateCustomer; \ No newline at end of file diff --git a/client/src/components/customers/CustomersList.js b/client/src/components/customers/CustomersList.js new file mode 100644 index 0000000..5f45c1d --- /dev/null +++ b/client/src/components/customers/CustomersList.js @@ -0,0 +1,240 @@ +import React, {useState, useEffect} from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { useNavigate, useParams } from "react-router-dom"; +import { customerSlice } from "./../../store"; +import { AuthService, CustomerService, EventsService } from "../../services"; +import { CUSTOMER_TYPE } from "../../shared"; +import { Spinner } from "react-bootstrap"; + + +const CustomersList = () => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const [customers, setCustomers] = useState([]); + const [keyword, setKeyword] = useState(''); + const [showInactive, setShowInactive] = useState(false); + const [transferMap, setTransferMap] = useState({}); + // const [events, setEvents] = useState([]); + const [showSpinner, setShowSpinner] = useState(false); + + useEffect(() => { + if (!AuthService.canViewCustomers()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + CustomerService.getAllCustomers().then((data) => { + setCustomers(data.data.sort((a, b) => a.lastname > b.lastname ? 1: -1)); + }) + // EventsService.getAllEvents({ from: EventsService.formatDate(new Date()), to: '9999-12-31'}).then((data) => { + // console.log('events', data.data) + // setEvents(data.data); + // }) + }, []); + + const redirectToAdmin = () => { + navigate(`/admin/customer-report`) + } + + const goToEdit = (id) => { + navigate(`/customers/edit/${id}`) + } + + const goToCreateNew = () => { + navigate(`/customers`) + } + + const setTransferValue = (customerId, site) => { + const currentMap = Object.assign({}, transferMap); + if (site !== undefined && site !== null && site !== '' && site !== 0) { + currentMap[customerId] = site; + setTransferMap(currentMap); + } else { + if (customerId) { + delete currentMap[customerId]; + setTransferMap(currentMap); + } + } + } + + const goToView = (id) => { + navigate(`/customers/${id}`) + } + const site = EventsService.site; + + const transferCustomer = (customerId) => { + console.log('transfer To', transferMap[customerId]); + if (site !== undefined && site !== null && site !== '' && site !== 0) { + setShowSpinner(true); + const currentCustomer = customers.find((c) => c.id === customerId); + if (currentCustomer) { + EventsService.getByCustomer({name: currentCustomer?.name, id: currentCustomer?.id, namecn: currentCustomer?.name_cn}).then((eventsData) => { + const events = eventsData?.data; + console.log('eventsToUpdate', events); + CustomerService.updateCustomer(customerId, { ...currentCustomer, site: transferMap[customerId] }).then(() => { + // const eventsWithCustomer = events.filter(ev => ev?.data?.customer === customerId || ev?.data?.client_name === currentCustomer?.name || ev?.target_name === currentCustomer?.name); + if (events?.length > 0) { + Promise.all(events?.map( + item => EventsService.updateEvent(item?.id, { + ...item, site: transferMap[customerId] + }))).then(() => { + CustomerService.getAllCustomers().then((data) => { + setCustomers(data.data?.sort((a, b) => a.lastname > b.lastname ? 1: -1)); + setShowSpinner(false); + }) + setShowSpinner(false); + }).catch((err) => setShowSpinner(false)) + } else { + CustomerService.getAllCustomers().then((data) => { + setCustomers(data.data?.sort((a, b) => a.lastname > b.lastname ? 1: -1)); + setShowSpinner(false); + }) + } + + }).catch(err => setShowSpinner(false)) + }) + } + + } + } + + const exportCSV = (customer) => { + const csvString = [ + [...Object.keys(customer)], // Specify your headers here + Object.keys(customer).map((key) => key && customer[key] && `"${customer[key]}"` || "") // Map your data fields accordingly + ] + .map(row => row.join(",")) + .join("\n"); + // Create a Blob from the CSV string + const blob = new Blob([csvString], { type: 'text/csv' }); + // Generate a download link and initiate the download + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = `customer_${customer.name}.csv`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + EventsService.getByCustomer({name: customer?.name, id: customer?.id, namecn: customer?.name_cn}).then((data) => { + const events = data.data; + if (events && events?.length > 0) { + const lastEle = events[events.length - 1] + const eventscsvString = [ + [...Object.keys(lastEle).filter(item => item !== 'data'), ...Object.keys(lastEle?.data)], + ...events.map((event) => { + return [ + ...Object.keys(lastEle).filter(item => item !== 'data').map((key) => event[key] && `"${event[key]}"` || ''), + ...Object.keys(lastEle?.data).map((key) => event?.data[key] && `"${event?.data[key]}"` || '') + ] + }) + ].map(row => row.join(",")) + .join("\n"); + // Create a Blob from the CSV string + const blobCSV = new Blob([eventscsvString], { type: 'text/csv' }); + // Generate a download link and initiate the download + const urlCSV = URL.createObjectURL(blobCSV); + const csvlink = document.createElement('a'); + csvlink.href = urlCSV; + csvlink.download = `Customer_${customer.name}_Medical_Events.csv`; + document.body.appendChild(csvlink); + csvlink.click(); + document.body.removeChild(csvlink); + URL.revokeObjectURL(urlCSV); + } else { + window.alert('No medical events found for this user') + } + + }) + } + + + return ( + <> + {showSpinner &&
+ + Loading... + +
} +
+
+
All Customers
+
+
+
+
+ Filter By Name: setKeyword(e.currentTarget.value)} /> +
+
+ setShowInactive(!showInactive)} /> + Show Transferred / Deactivated Customers +
+
+ + + + + + + + + + + + + + + + + + + + + + + { + customers && customers.filter((item) => item?.name.toLowerCase().includes(keyword.toLowerCase())).filter(item => showInactive ? item : item.type !== CUSTOMER_TYPE.TRANSFERRED && item.type!=CUSTOMER_TYPE.DECEASED && item.type!=CUSTOMER_TYPE.DISCHARED && item.status === 'active').map(customer => + + + + + + + + + + + + + + + + ) + } + +
NamePreferred NameEmailTypePickup StatusDate of BirthGenderLanguageMedicare NumberMedicaid NumberAddressPhoneEmergency ContactTransfer To
{customer?.name}{customer?.name_cn}{customer?.email}{customer?.type}{customer?.pickup_status}{customer?.birth_date}{customer?.gender}{customer?.language}{customer?.medicare_number}{customer?.medicaid_number}{customer?.address1 || customer?.address2 || customer?.address3 || customer?.address4 || customer?.address5}{customer?.phone || customer?.home_phone || customer?.mobile_phone}{customer?.emergency_contact} + {AuthService.canAddOrEditCustomers() && } + {AuthService.canViewCustomers() && } + {AuthService.canViewCustomers() && } + + {AuthService.canAddOrEditCustomers() && +
+ + { transferMap[customer?.id] && } +
+ + } +
+ +
+
+ + ) +}; + +export default CustomersList; \ No newline at end of file diff --git a/client/src/components/customers/UpdateCustomer.js b/client/src/components/customers/UpdateCustomer.js new file mode 100644 index 0000000..49cd598 --- /dev/null +++ b/client/src/components/customers/UpdateCustomer.js @@ -0,0 +1,576 @@ +import React, {useState, useEffect} from "react"; +import { useDispatch } from "react-redux"; +import { useNavigate, useParams } from "react-router-dom"; +import { customerSlice } from "./../../store"; +import { AuthService, CustomerService, ResourceService } from "../../services"; +import Select from 'react-select'; +import { Modal, Button } from "react-bootstrap"; +import { CUSTOMER_TYPE, PICKUP_STATUS, PICKUP_STATUS_TEXT , CUSTOMER_TYPE_TEXT} from "../../shared"; + +const UpdateCustomer = () => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const { updateCustomer } = customerSlice.actions; + const urlParams = useParams(); + const [currentCustomer, setCurrentCustomer] = useState(undefined); + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [firstname, setFirstname] = useState(''); + const [lastname, setLastname] = useState(''); + const [nameCN, setNameCN] = useState(''); + const [birthDate, setBirthDate] = useState(''); + const [email, setEmail] = useState(''); + const [mobilePhone, setMobilePhone] = useState(''); + const [phone, setPhone] = useState(''); + const [homePhone, setHomePhone] = useState(''); + const [tableId, setTableId] = useState(''); + const [language, setLanguage] = useState(''); + const [address1, setAddress1] = useState(''); + const [address2, setAddress2] = useState(''); + const [address3, setAddress3] = useState(''); + const [address4, setAddress4] = useState(''); + const [address5, setAddress5] = useState(''); + const [customerType, setCustomerType] = useState(''); + const [selectedFile, setSelectedFile] = useState(); + const [careProvider, setCareProvider] = useState(''); + const [medicareNumber, setMedicareNumber] = useState(''); + const [medicaidNumber, setMedicaidNumber] = useState(''); + const [pharmacy, setPharmacy] = useState(''); + const [pharmacyId, setPharmacyId] = useState(''); + const [pickupStatus, setPickupStatus] = useState(''); + const [specialNeeds, setSpecialNeeds] = useState(''); + const [emergencyContact, setEmergencyContact] = useState(''); + const [admissionDate, setAdmissionDate] = useState(''); + const [vehicleNo, setVehicleNo] = useState(''); + const [note, setNote] = useState(''); + const [pin, setPin] = useState(''); + const [seating, setSeating] = useState(''); + const [caller, setCaller] = useState(''); + const [dischargeDate, setDischargeDate] = useState(''); + const [placement, setPlacement] = useState(''); + const [nickname, setNickname] = useState(''); + const [groups, setGroups] = useState(''); + const [tags, setTags] = useState(''); + const [roles, setRoles] = useState(''); + const [apartment, setApartment] = useState(''); + const [resources, setResources] = useState([]); + const [showDeleteModal, setShowDeleteModal] = useState(false); + const [privateNote, setPrivateNote] = useState(''); + const [disability, setDisability] = useState(false); + const [gender, setGender] = useState(''); + const [weight, setWeight] = useState(''); + const [heightFeet, setHeightFeet] = useState(''); + const [heightInch, setHeightInch] = useState(''); + const [textMsgEnabled, setTextMsgEnabled] = useState(false); + const params = new URLSearchParams(window.location.search); + const redirectTo = () => { + navigate(`/customers/list`); + } + const redirectToView = () => { + navigate(`/customers/${urlParams.id}`); + } + + useEffect(() => { + if (!AuthService.canAddOrEditCustomers()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + if (!currentCustomer) { + CustomerService.getCustomer(urlParams.id).then((data) => { + setCurrentCustomer(data.data); + }) + } + ResourceService.getAll('pharmacy').then(data => { + setResources(data.data); + }) + }, []); + + const onPharmacyChange = (selectedPharmacy) => { + setPharmacy(selectedPharmacy); + setPharmacyId(selectedPharmacy?.value); + } + + useEffect(() => { + if (currentCustomer) { + setUsername(currentCustomer.username); + setFirstname(currentCustomer.firstname); + setLastname(currentCustomer.lastname); + setNameCN(currentCustomer.name_cn); + setBirthDate(currentCustomer.birth_date); + setRoles(currentCustomer.roles?.join(',')); + setEmail(currentCustomer.email); + setMobilePhone(currentCustomer.mobile_phone); + setPhone(currentCustomer.phone); + setHomePhone(currentCustomer.home_phone); + setLanguage(currentCustomer.language); + setAddress1(currentCustomer.address1); + setAddress2(currentCustomer.address2); + setAddress3(currentCustomer.address3); + setAddress4(currentCustomer.address4); + setAddress5(currentCustomer.address5); + setNote(currentCustomer.note); + setTableId(currentCustomer.table_id); + setCustomerType(currentCustomer.type); + setCareProvider(currentCustomer.care_provider); + setMedicareNumber(currentCustomer.medicare_number); + setMedicaidNumber(currentCustomer.medicaid_number); + setPharmacy({label: currentCustomer.pharmacy, value: currentCustomer.pharmacy_id}); + setPharmacyId(currentCustomer.pharmacy_id); + setPickupStatus(currentCustomer.pickup_status); + setSpecialNeeds(currentCustomer.setSpecial_needs); + setEmergencyContact(currentCustomer.emergency_contact); + setAdmissionDate(currentCustomer.admission_date); + setDischargeDate(currentCustomer.discharge_date); + setVehicleNo(currentCustomer.vehicle_no); + setPin(currentCustomer.pin); + setSeating(currentCustomer.seating); + setCaller(currentCustomer.caller); + setPlacement(currentCustomer.placement); + setNickname(currentCustomer.nickname); + setGroups(currentCustomer.groups?.join(',')); + setTags(currentCustomer.tags?.join(',')); + setApartment(currentCustomer.apartment); + setPrivateNote(currentCustomer.private_note); + setDisability(currentCustomer.disability ? 'true' : 'false'); + setWeight(currentCustomer.weight); + setGender(currentCustomer.gender); + setTextMsgEnabled(currentCustomer.text_msg_enabled ? 'true': 'false'); + setHeightFeet(currentCustomer.height && currentCustomer.height.length > 0? currentCustomer.height.replaceAll(' ', '')?.split('ft')[0] : ''); + setHeightInch(currentCustomer.height && currentCustomer.height.length > 0? currentCustomer.height.replaceAll(' ', '')?.split('ft')[1]?.split('in')[0] : ''); + } + + }, [currentCustomer]); + + const triggerShowDeleteModal = () => { + setShowDeleteModal(true); + } + + const deleteCustomer = () => { + let data = { + username, + name: customerType === 'discharged' ? `${lastname},${firstname} (discharged)` : `${lastname},${firstname}`, + name_cn: nameCN, + email, + mobile_phone: mobilePhone, + phone, + table_id: tableId, + home_phone: homePhone, + type: customerType, + language, + status: 'inactive', + address1, + address2, + address3, + address4, + address5, + firstname, + lastname, + birth_date: birthDate, + care_provider: careProvider, + medicare_number: medicareNumber, + medicaid_number: medicaidNumber, + pharmacy: pharmacy?.label || '', + pharmacy_id: pharmacyId, + pickup_status: pickupStatus, + special_needs: specialNeeds, + emergency_contact: emergencyContact, + admission_date: admissionDate, + vehicle_no: vehicleNo, + discharge_date: dischargeDate, + edit_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name || '', + note, + pin, + seating, + caller, + placement, + nickname, + groups: groups?.replace(' ', '').split(','), + tags: tags?.replace(' ', '').split(','), + roles: roles?.replace(' ', '').split(','), + apartment, + private_note: privateNote, + disability: disability === 'true' ? true : false, + weight, + gender, + height: ((heightFeet?.length > 0 && heightInch?.length> 0 && `${heightFeet} ft ${heightInch} in`)) || '', + text_msg_enabled: textMsgEnabled === 'true' || false + }; + // const dataForLegacy = { + // username, + // name: customerType === 'discharged' ? `${lastname},${firstname} (discharged)` : `${lastname},${firstname}`, + // name_cn: `${lastname},${firstname} ${nameCN}`, + // email, + // password, + // mobile_phone: mobilePhone, + // phone, + // table_id: tableId, + // home_phone: homePhone, + // type: customerType, + // language, + // status: 'active', + // address: address1, + // address2, + // firstname, + // lastname, + // birth_date: birthDate, + // care_provider: careProvider, + // medicare_number: medicareNumber, + // medicaid_number: medicaidNumber, + // pharmacy: pharmacy?.label || '', + // pharmacy_id: pharmacyId, + // emergency_contact: emergencyContact, + // admission_date: admissionDate, + // vehicle_no: vehicleNo, + // discharge_date: dischargeDate, + // edit_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name || '', + // note, + // pin, + // seating, + // caller, + // placement, + // nickname, + // groups: groups.replace(' ', '').split(','), + // tags: tags.replace(' ', '').split(','), + // roles: roles.replace(' ', '').split(','), + // private_note: privateNote + // }; + if (password && password.length > 0) { + data = Object.assign({}, data, {password}); + // dataForLegacy = Object.assign({}, dataForLegacy, {password}); + } + const formData = new FormData(); + formData.append("file", selectedFile); + let payload = {id: urlParams.id, data, currentCustomer}; + if (selectedFile) { + console.log('formData'); + payload = Object.assign({}, payload, {avatar: formData}) + } + dispatch(updateCustomer(payload)); + setShowDeleteModal(false); + redirectTo(); + }; + + + const saveCustomer = () => { + let data = { + username, + name: customerType === 'discharged' ? `${lastname},${firstname} (discharged)` : `${lastname},${firstname}`, + name_cn: nameCN, + email, + mobile_phone: mobilePhone, + phone, + table_id: tableId, + home_phone: homePhone, + type: customerType, + language, + status: 'active', + address1, + address2, + address3, + address4, + address5, + firstname, + lastname, + birth_date: birthDate, + care_provider: careProvider, + medicare_number: medicareNumber, + medicaid_number: medicaidNumber, + pharmacy: pharmacy?.label || '', + pharmacy_id: pharmacyId, + pickup_status: pickupStatus, + special_needs: specialNeeds, + emergency_contact: emergencyContact, + admission_date: admissionDate, + vehicle_no: vehicleNo, + discharge_date: dischargeDate, + edit_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name || '', + note, + pin, + seating, + caller, + placement, + nickname, + groups: groups?.replace(' ', '').split(','), + tags: tags?.replace(' ', '').split(','), + roles: roles?.replace(' ', '').split(','), + apartment, + private_note: privateNote, + disability: disability === 'true' ? true : false, + weight, + gender, + height: `${heightFeet} ft ${heightInch} in`, + text_msg_enabled: textMsgEnabled === 'true' || false + }; + // const dataForLegacy = { + // username, + // name: customerType === 'discharged' ? `${lastname},${firstname} (discharged)` : `${lastname},${firstname}`, + // name_cn: `${lastname},${firstname} ${nameCN}`, + // email, + // password, + // mobile_phone: mobilePhone, + // phone, + // table_id: tableId, + // home_phone: homePhone, + // type: customerType, + // language, + // status: 'active', + // address: address1, + // address2, + // firstname, + // lastname, + // birth_date: birthDate, + // care_provider: careProvider, + // medicare_number: medicareNumber, + // medicaid_number: medicaidNumber, + // pharmacy: pharmacy?.label || '', + // pharmacy_id: pharmacyId, + // emergency_contact: emergencyContact, + // admission_date: admissionDate, + // vehicle_no: vehicleNo, + // discharge_date: dischargeDate, + // edit_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name || '', + // note, + // pin, + // seating, + // caller, + // placement, + // nickname, + // groups: groups.replace(' ', '').split(','), + // tags: tags.replace(' ', '').split(','), + // roles: roles.replace(' ', '').split(','), + // private_note: privateNote + // }; + + if (password && password.length > 0) { + data = Object.assign({}, data, {password}); + // dataForLegacy = Object.assign({}, dataForLegacy, {password}); + } + const formData = new FormData(); + formData.append("file", selectedFile); + let payload = {id: urlParams.id, data, currentCustomer}; + if (selectedFile) { + console.log('file', selectedFile); + console.log('formData', formData); + payload = Object.assign({}, payload, {avatar: formData}) + } + dispatch(updateCustomer(payload)); + setTimeout(() => { + redirectToView(); + }, 3000); + + }; + + const closeDeleteModal = () => { + setShowDeleteModal(false); + } + + + return ( + <> +
+
+
Update Customer
+
+
+
+
+
First Name:(*)
setFirstname(e.target.value)}/> +
+
+
Last Name:(*)
setLastname(e.target.value)}/> +
+
+
Preferred Name (中文姓名):
setNameCN(e.target.value)}/> +
+
+
Email:(*)
setEmail(e.target.value)}/> +
+
+
Care Provider:
setCareProvider(e.target.value)}/> +
+
+
Emergency Contact:
setEmergencyContact(e.target.value)}/> +
+
+
Medicare Number:
setMedicareNumber(e.target.value)}/> +
+
+
Medicaid Number:
setMedicaidNumber(e.target.value)}/> +
+
+
Pharmacy:
+ +
+
+
Pharmacy ID:
setPharmacyId(e.target.value)}/> +
+
+
Address 1:
setAddress1(e.target.value)}/> +
+
+
Address 2:
setAddress2(e.target.value)}/> +
+
+
Address 3:
setAddress3(e.target.value)}/> +
+
+
Address 4:
setAddress4(e.target.value)}/> +
+
+
Address 5:
setAddress5(e.target.value)}/> +
+
+
Apartment:
setApartment(e.target.value)}/> +
+
+
Table Id:
setTableId(e.target.value)}/> +
+
+
Customer Type:
+
+
+
Pickup Status:
+
+
+
Upload Avatar (Image size should be less than 500 KB):
+ setSelectedFile(e.target.files[0])} + className="form-control-file border" + /> + +
+
+
Birth Date (Type in as: MM/DD/YYYY):
setBirthDate(e.target.value)}/> +
+
+
Phone :
setPhone(e.target.value)}/> +
+
+
Mobile Phone:
setMobilePhone(e.target.value)}/> +
+
+
Home Phone:
setHomePhone(e.target.value)}/> +
+
+
Special Needs:
setSpecialNeeds(e.target.value)}/> +
+
+
Language(Please use ',' between each language):
setLanguage(e.target.value)}/> +
+
+
Username (not required):
setUsername(e.target.value)}/> +
+
+
Password (not required):
setPassword(e.target.value)}/> +
+
+
Nick Name:
setNickname(e.target.value)}/> +
+
+
Note:
setNote(e.target.value)}/> +
+ +
+
Admission Date(Type in as 'MM/DD/YYYY'):
setAdmissionDate(e.target.value)}/> +
+
+
Discharge Date(Type in as 'MM/DD/YYYY'):
setDischargeDate(e.target.value)}/> +
+
+
Pin:
setPin(e.target.value)}/> +
+
+
Seating:
setSeating(e.target.value)}/> +
+
+
Vehicle No:
setVehicleNo(e.target.value)}/> +
+
+
Caller:
setCaller(e.target.value)}/> +
+
+
Placement:
setPlacement(e.target.value)}/> +
+
+
Groups(Please use ',' between each group):
setGroups(e.target.value)}/> +
+
+
Tags(Please use ',' between each tags):
setTags(e.target.value)}/> +
+
+
Roles(Please use ',' between each roles):
setRoles(e.target.value)}/> +
+
+
Private Note:
setPrivateNote(e.target.value)}/> +
+
+
Disability:
+
+
+
Height:
+ setHeightFeet(e.target.value)}/> Ft + setHeightInch(e.target.value)}/> In +
+
+
Weight:
setWeight(e.target.value)}/> lb +
+
+
Gender:
+
+
+
Enable Text Message:
+
+
+
+
+ + + +
+
+ closeDeleteModal()}> + + Delete Customer + + +
Are you sure you want to delete this customer?
+
+ + + + +
+ + ); +}; + +export default UpdateCustomer; \ No newline at end of file diff --git a/client/src/components/customers/ViewCustomer.js b/client/src/components/customers/ViewCustomer.js new file mode 100644 index 0000000..3f375c5 --- /dev/null +++ b/client/src/components/customers/ViewCustomer.js @@ -0,0 +1,185 @@ +import React, {useState, useEffect} from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { AuthService, CustomerService } from "../../services"; + +const ViewCustomer = () => { + const navigate = useNavigate(); + + const urlParams = useParams(); + const [currentCustomer, setCurrentCustomer] = useState(undefined); + const [currentAvatar, setCurrentAvatar] = useState(undefined); + + const redirectTo = () => { + navigate(`/customers/list`) + } + + useEffect(() => { + if (!AuthService.canViewCustomers()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + if (!currentCustomer) { + CustomerService.getCustomer(urlParams.id).then((data) => { + setCurrentCustomer(data.data); + }) + } + }, []); + + useEffect(() => { + if (currentCustomer?.id ) { + CustomerService.getAvatar(currentCustomer?.id).then((data) => { + setCurrentAvatar(data.data); + }) + } + }, [currentCustomer]); + + return ( + <> +
+
+
{currentCustomer?.name}
+
+
+
+
+ {currentAvatar && } +
+
+
Name: {currentCustomer?.name}
+
+
+
First Name: {currentCustomer?.firstname}
+
+
+
Last Name: {currentCustomer?.lastname}
+
+
+
Preferred Name (中文姓名): {currentCustomer?.name_cn}
+
+
+
Birth Date: {currentCustomer?.birth_date}
+
+
+
Table Id:{currentCustomer?.table_id}
+
+
+
Customer Type:{currentCustomer?.type}
+
+
+
Pickup Status:{currentCustomer?.pickup_status}
+
+
+
Email: {currentCustomer?.email}
+
+
+
Care Provider:{currentCustomer?.care_provider}
+
+
+
Emergency Contact:{currentCustomer?.emergency_contact}
+
+
+
Medicare Number:{currentCustomer?.medicare_number}
+
+
+
Medicaid Number:{currentCustomer?.medicaid_number}
+
+
+
Pharmacy:{currentCustomer?.pharmacy}
+
+
+
Pharmacy ID:{currentCustomer?.pharmacy_id}
+
+
+
Phone: {currentCustomer?.phone}
+
+
+
Mobile Phone: {currentCustomer?.mobile_phone}
+
+
+
Home Phone: {currentCustomer?.home_phone}
+
+
+
Special Needs:{currentCustomer?.special_needs}
+
+
+
Language: {currentCustomer?.language}
+
+
+
Username: {currentCustomer?.username}
+
+
+
Address 1: {currentCustomer?.address1}
+
+
+
Address 2: {currentCustomer?.address2}
+
+
+
Address 3: {currentCustomer?.address3}
+
+
+
Address 4: {currentCustomer?.address4}
+
+
+
Address 5: {currentCustomer?.address5}
+
+
+
Apartment: {currentCustomer?.apartment}
+
+
+
Note: {currentCustomer?.note}
+
+
+
Admission Date: {currentCustomer?.admission_date}
+
+
+
Discharge Date: {currentCustomer?.discharge_date}
+
+
+
Pin:{currentCustomer?.pin}
+
+
+
Seating:{currentCustomer?.seating}
+
+
+
Vehicle No: {currentCustomer?.vehicle_no}
+
+
+
Caller:{currentCustomer?.caller}
+
+
+
Placement:{currentCustomer?.placement}
+
+
+
Groups:{currentCustomer?.groups?.join(', ')}
+
+
+
Tags:{currentCustomer?.tags?.join(', ')}
+
+
+
Roles:{currentCustomer?.roles?.join(', ')}
+
+
+
Private Note:{currentCustomer?.private_note}
+
+
+
Disability:{currentCustomer?.disability ? 'Yes' : 'No'}
+
+
+
Gender:{currentCustomer?.gender}
+
+
+
Height:{currentCustomer?.height}
+
+
+
Weight:{currentCustomer?.weight} lb
+
+
+
Text Message Enabled:{currentCustomer?.text_msg_enabled ? 'Yes': 'No'}
+
+
+ + ); +}; + +export default ViewCustomer; \ No newline at end of file diff --git a/client/src/components/employees/CreateEmployee.js b/client/src/components/employees/CreateEmployee.js new file mode 100644 index 0000000..77621d5 --- /dev/null +++ b/client/src/components/employees/CreateEmployee.js @@ -0,0 +1,237 @@ +import React, {useState, useEffect} from "react"; +import { useDispatch } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import { driverSlice } from "./../../store"; +import { employeeSlice } from "../../store/employees"; +import { AuthService, DispatcherService, EmployeeService } from "../../services"; +import { EMPLOYEE_TITLE, EMPLOYEE_TITLE_CN, EMPLOYEE_TITLE_ROLES_MAP, INVITATION_CODE } from "../../shared"; + +const CreateEmployee = () => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const { createDriver } = driverSlice.actions; + const { createEmployee} = employeeSlice.actions; + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [firstname, setFirstname] = useState(''); + const [lastname, setLastname] = useState(''); + const [nameCN, setNameCN] = useState(''); + const [birthDate, setBirthDate] = useState(''); + const [roles, setRoles] = useState(''); + const [email, setEmail] = useState(''); + const [driverCapacity, setDriverCapacity] = useState(); + const [mobilePhone, setMobilePhone] = useState(''); + const [phone, setPhone] = useState(''); + const [homePhone, setHomePhone] = useState(''); + const [language, setLanguage] = useState(''); + const [employmentStatus, setEmploymentStatus] = useState(''); + const [address, setAddress] = useState(''); + const [title, setTitle] = useState(''); + const [titleCN, setTitleCN] = useState(''); + const [department, setDepartment] = useState(''); + const [dateHired, setDateHired] = useState(''); + const [note, setNote] = useState(''); + const [tags, setTags] = useState(''); + const [showSaveInfo, setShowSaveInfo] = useState(false); + const [invitationCode, setInvitationCode] = useState(''); + const params = new URLSearchParams(window.location.search); + const redirectTo = () => { + const redirect = params.get('redirect'); + const type = params.get('type'); + if (redirect === 'schedule') { + navigate(`/trans-routes/schedule`); + } else { + if (type === 'driver') { + navigate(`/trans-routes/dashboard`); + } else { + navigate('/admin'); + } + } + } + + const goToLogin = () => { + navigate(`/login`); + } + + const setTitleAndRoles = (value) => { + if (value) { + setTitle(value); + setTitleCN(EMPLOYEE_TITLE_CN[value]); + setRoles(EMPLOYEE_TITLE_ROLES_MAP[value]?.join(',')); + } else { + setTitle(''); + setTitleCN(''); + setRoles(''); + } + } + + useEffect(() => { + if ((!AuthService.canAddOrEditEmployees() && !(AuthService.canCreateOrEditDrivers() && params.get('type')==='driver') && params.get('type') !== 'dispatcher' )) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + if (params.get('type') === 'driver' || params.get('type') === 'dispatcher') { + setTitleAndRoles('DRIVER'); + } + }, []) + + const saveEmployee = () => { + if (params.get('type') === 'dispatcher' && !INVITATION_CODE.includes(invitationCode)) { + window.alert('Invitation Code is not correct') + } else { + if (!EmployeeService.validatePassword(password)) { + window.alert("Password's length must be longer than 8. It must contain 1 uppercase character, 1 lowercase character and 1 special character.") + } else { + const data = { + username, + name: `${lastname},${firstname}`, + name_cn: nameCN, + email, + password, + mobile_phone: mobilePhone, + phone, + home_phone: homePhone, + language, + employment_status: employmentStatus, + status: 'active', + address, + title, + title_cn: titleCN, + firstname, + lastname, + department, + birth_date: birthDate, + driver_capacity: driverCapacity, + date_hired: dateHired, + create_by: 'admin', + edit_by: 'admin', + note, + tags: tags.replace(' ', '').split(','), + roles: roles && roles.replace(' ', '').split(',') + }; + if (params.get('type') === 'driver') { + dispatch(createDriver({data})); + } else { + if (params.get('type') === 'dispatcher') { + DispatcherService.createNewDispatcher(data).then(() => { + setShowSaveInfo(true); + }); + } else { + dispatch(createEmployee({data})); + } + + } + if (params.get('type') !== 'dispatcher') { + redirectTo(); + } + } + } + }; + + + return ( + <> +
+
+
Create New Employee
+
+ {showSaveInfo &&
+ Dispatcher Info Created Successfully. Please contact Admin to Activate your Account before login. + +
} +
+
+ { params.get('type') === 'dispatcher' &&
+
Invitation Code(*):
setInvitationCode(e.target.value)}/> +
} +
+
Username (used for login)(*):
setUsername(e.target.value)}/> +
+
+
Password (used for login)(*):
setPassword(e.target.value)}/> +
+
+
First Name:
setFirstname(e.target.value)}/> +
+
+
Last Name:
setLastname(e.target.value)}/> +
+
+
Preferred Name (中文姓名):
setNameCN(e.target.value)}/> +
+
+
Birth Date (Type in as: MM/DD/YYYY):
setBirthDate(e.target.value)}/> +
+
+
Title(*):
+
+
+
Title CN(中文称谓):
setTitleCN(e.target.value)}/> +
+
+
Roles(*):
setRoles(e.target.value)}/> +
+
+
Email(*):
setEmail(e.target.value)}/> +
+
+
Driver Capacity:
setDriverCapacity(e.target.value)}/> +
+
+
Phone :
setPhone(e.target.value)}/> +
+
+
Mobile Phone:
setMobilePhone(e.target.value)}/> +
+
+
Home Phone:
setHomePhone(e.target.value)}/> +
+
+
Language(Please use ',' between each language):
setLanguage(e.target.value)}/> +
+
+
Employment Type:
+
+
+
Address:
setAddress(e.target.value)}/> +
+
+
Department:
setDepartment(e.target.value)}/> +
+
+
Date Hired(Type in as 'MM/DD/YYYY'):
setDateHired(e.target.value)}/> +
+
+
Note:
setNote(e.target.value)}/> +
+
+
Tags(Please use ',' between each tags):
setTags(e.target.value)}/> +
+ +
+
+
+ + +
+ {showSaveInfo &&
+ Dispatcher Info Created Successfully. Please contact Admin to Activate your Account before login. + +
} +
+ + ); +}; + +export default CreateEmployee; \ No newline at end of file diff --git a/client/src/components/employees/EmployeeList.js b/client/src/components/employees/EmployeeList.js new file mode 100644 index 0000000..56c1137 --- /dev/null +++ b/client/src/components/employees/EmployeeList.js @@ -0,0 +1,88 @@ +import React, {useState, useEffect} from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { useNavigate, useParams } from "react-router-dom"; +import { driverSlice } from "./../../store"; +import { employeeSlice } from "../../store/employees"; +import { AuthService, EmployeeService } from "../../services"; + +const EmployeeList = () => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const [employees, setEmployees] = useState([]); + const [keyword, setKeyword] = useState(''); + const [showInactive, setShowInactive] = useState(false); + + + useEffect(() => { + if (!AuthService.canViewEmployees()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + EmployeeService.getAllEmployees().then((data) => + setEmployees(data.data) + ); + }, []); + + const redirectToAdmin = () => { + navigate(`/admin/customer-report`) + } + + const goToEdit = (id) => { + navigate(`/employees/edit/${id}`) + } + + + const goToView = (id) => { + navigate(`/employees/${id}`) + } + + + return ( + <> +
+
+
All Employees
+
+
+
+
+
Filter By Name: setKeyword(e.currentTarget.value)}/>
+ setShowInactive(!showInactive)} /> + Show Inactive Employees + + + + + + + + + + + + + + { + employees && employees.filter(item => showInactive ? item : item.status === 'active').filter((item)=> item?.name.toLowerCase().includes(keyword.toLowerCase())).map(employee => + + + + + + + ) + } + +
NamePreferred NameUsernameStatusRoles
{employee?.name}{employee?.name_cn}{employee?.username}{employee?.status}{employee?.roles?.join(', ')} + {AuthService.canAddOrEditEmployees() && } + {AuthService.canViewEmployees() && } +
+ +
+
+ + ) +}; + +export default EmployeeList; \ No newline at end of file diff --git a/client/src/components/employees/UpdateEmployee.js b/client/src/components/employees/UpdateEmployee.js new file mode 100644 index 0000000..bf015ea --- /dev/null +++ b/client/src/components/employees/UpdateEmployee.js @@ -0,0 +1,319 @@ +import React, {useState, useEffect} from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { useNavigate, useParams } from "react-router-dom"; +import { driverSlice } from "./../../store"; +import { employeeSlice } from "../../store/employees"; +import { AuthService, EmployeeService } from "../../services"; +import { EMPLOYEE_TITLE, EMPLOYEE_TITLE_CN, EMPLOYEE_TITLE_ROLES_MAP } from "../../shared"; +import { Modal, Button } from "react-bootstrap"; + +const UpdateEmployee = () => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const { updateDriver } = driverSlice.actions; + const { updateEmployee } = employeeSlice.actions; + const urlParams = useParams(); + const drivers = useSelector((state) => state.drivers && state.drivers.drivers); + const [currentEmployee, setCurrentEmployee] = useState(drivers.find((driver) => driver.id === urlParams.id)); + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(undefined); + const [firstname, setFirstname] = useState(''); + const [lastname, setLastname] = useState(''); + const [nameCN, setNameCN] = useState(''); + const [birthDate, setBirthDate] = useState(''); + const [roles, setRoles] = useState(''); + const [email, setEmail] = useState(''); + const [driverCapacity, setDriverCapacity] = useState(); + const [mobilePhone, setMobilePhone] = useState(''); + const [phone, setPhone] = useState(''); + const [homePhone, setHomePhone] = useState(''); + const [language, setLanguage] = useState(''); + const [employmentStatus, setEmploymentStatus] = useState(''); + const [address, setAddress] = useState(''); + const [title, setTitle] = useState(''); + const [titleCN, setTitleCN] = useState(''); + const [department, setDepartment] = useState(''); + const [dateHired, setDateHired] = useState(''); + const [note, setNote] = useState(''); + const [status, setStatus] = useState(''); + const [tags, setTags] = useState(''); + const [showDeleteModal, setShowDeleteModal] = useState(false); + const params = new URLSearchParams(window.location.search); + const redirectTo = () => { + const redirect = params.get('redirect'); + const type = params.get('type'); + if (redirect === 'schedule') { + navigate(`/trans-routes/schedule`); + } else { + if (type === 'driver') { + navigate(`/trans-routes/dashboard`); + } else { + navigate('/employees/list'); + } + } + } + + useEffect(() => { + if (!AuthService.canAddOrEditEmployees() && !(AuthService.canCreateOrEditDrivers() && params.get('type')==='driver' )) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + if (!currentEmployee) { + EmployeeService.getEmployee(urlParams.id).then((data) => { + setCurrentEmployee(data.data); + }) + } + }, []); + + useEffect(() => { + if (currentEmployee) { + setUsername(currentEmployee.username); + setFirstname(currentEmployee.firstname); + setLastname(currentEmployee.lastname); + setNameCN(currentEmployee.name_cn); + setBirthDate(currentEmployee.birth_date); + setRoles(currentEmployee.roles.join(',')); + setEmail(currentEmployee.email); + setDriverCapacity(currentEmployee.driver_capacity); + setMobilePhone(currentEmployee.mobile_phone); + setPhone(currentEmployee.phone); + setHomePhone(currentEmployee.home_phone); + setLanguage(currentEmployee.language); + setEmploymentStatus(currentEmployee.employment_status); + setAddress(currentEmployee.address); + setTitle(currentEmployee.title); + setTitleCN(currentEmployee.title_cn); + setDepartment(currentEmployee.department); + setDateHired(currentEmployee.date_hired); + setNote(currentEmployee.note); + setStatus(currentEmployee.status); + setTags(currentEmployee.tags?.join(',')); + } + + }, [currentEmployee]); + + const setTitleAndRoles = (value) => { + if (value) { + setTitle(value); + setTitleCN(EMPLOYEE_TITLE_CN[value]); + setRoles(EMPLOYEE_TITLE_ROLES_MAP[value]?.join(',')); + } else { + setTitle(''); + setTitleCN(''); + setRoles(''); + } + } + + const triggerShowDeleteModal = () => { + setShowDeleteModal(true); + } + + const deleteEmployee = () => { + let data = { + username, + name: `${lastname},${firstname}`, + name_cn: nameCN, + email, + mobile_phone: mobilePhone, + phone, + home_phone: homePhone, + language, + employment_status: employmentStatus, + status: 'terminated', + address, + title, + title_cn: titleCN, + firstname, + lastname, + department, + birth_date: birthDate, + driver_capacity: driverCapacity, + date_hired: dateHired, + create_by: 'admin', + edit_by: 'admin', + note, + tags: tags.replace(' ', '').split(','), + roles: roles && roles.replace(' ', '').split(',') + }; + if (password && password.length > 0) { + data = Object.assign({}, data, {password}); + } + if (params.get('type') === 'driver') { + dispatch(updateDriver({id: urlParams.id, data, currentEmployee})); + } else { + dispatch(updateEmployee({id: urlParams.id, data, currentEmployee})); + } + setShowDeleteModal(false); + redirectTo(); + }; + + + const saveEmployee = () => { + let data = { + username, + name: `${lastname},${firstname}`, + name_cn: nameCN, + email, + mobile_phone: mobilePhone, + phone, + home_phone: homePhone, + language, + employment_status: employmentStatus, + address, + title, + title_cn: titleCN, + firstname, + lastname, + department, + birth_date: birthDate, + driver_capacity: driverCapacity, + date_hired: dateHired, + create_by: 'admin', + edit_by: 'admin', + note, + tags: tags.replace(' ', '').split(','), + roles: roles && roles.replace(' ', '').split(','), + status + }; + if (password && password.length > 0) { + if (EmployeeService.validatePassword(password)) { + data = Object.assign({}, data, {password}); + } else { + window.alert("Password's length must be longer than 8. It must contain 1 uppercase character, 1 lowercase character and 1 special character."); + return; + } + + } + + if (params.get('type') === 'driver') { + dispatch(updateDriver({id: urlParams.id, data, currentEmployee})); + } else { + dispatch(updateEmployee({id: urlParams.id, data, currentEmployee})); + } + + redirectTo(); + }; + + const closeDeleteModal = () => { + setShowDeleteModal(false); + } + + return ( + <> +
+
+
Update Employee
+
+
+
+
+
Status:
+
+ {AuthService.isAdmin() &&
+
Username (used for login)(*):
setUsername(e.target.value)}/> +
} + {AuthService.isAdmin() &&
+
Password (used for login):
setPassword(e.target.value)}/> +
} +
+
First Name:
setFirstname(e.target.value)}/> +
+
+
Last Name:
setLastname(e.target.value)}/> +
+
+
Preferred Name (中文姓名):
setNameCN(e.target.value)}/> +
+
+
Birth Date (Type in as: MM/DD/YYYY):
setBirthDate(e.target.value)}/> +
+
+
Title:(*)
+
+
+
Title CN(中文称谓):
setTitleCN(e.target.value)}/> +
+
+
Roles:(*)
setRoles(e.target.value)}/> +
+
+
Email:(*)
setEmail(e.target.value)}/> +
+
+
Driver Capacity:
setDriverCapacity(e.target.value)}/> +
+
+
Phone :
setPhone(e.target.value)}/> +
+
+
Mobile Phone:
setMobilePhone(e.target.value)}/> +
+
+
Home Phone:
setHomePhone(e.target.value)}/> +
+
+
Language(Please use ',' between each language):
setLanguage(e.target.value)}/> +
+
+
Employment Type:
+
+
+
Address:
setAddress(e.target.value)}/> +
+
+
Department:
setDepartment(e.target.value)}/> +
+
+
Date Hired(Type in as 'MM/DD/YYYY'):
setDateHired(e.target.value)}/> +
+
+
Note:
setNote(e.target.value)}/> +
+
+
Tags(Please use ',' between each tags):
setTags(e.target.value)}/> +
+
+
+
+ + + +
+
+ closeDeleteModal()}> + + Delete User + + +
Are you sure you want to delete this user?
+
+ + + + +
+ + ); +}; + +export default UpdateEmployee; \ No newline at end of file diff --git a/client/src/components/employees/ViewEmployee.js b/client/src/components/employees/ViewEmployee.js new file mode 100644 index 0000000..66c69ea --- /dev/null +++ b/client/src/components/employees/ViewEmployee.js @@ -0,0 +1,100 @@ +import React, {useState, useEffect} from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { useNavigate, useParams } from "react-router-dom"; +import { AuthService, EmployeeService } from "../../services"; +import { EMPLOYEE_TITLE } from "../../shared"; + +const ViewEmployee = () => { + const navigate = useNavigate(); + + const urlParams = useParams(); + const [currentEmployee, setCurrentEmployee] = useState(undefined); + + const redirectTo = () => { + navigate(`/employees/list`) + } + + useEffect(() => { + if (!AuthService.canViewEmployees()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + if (!currentEmployee) { + EmployeeService.getEmployee(urlParams.id).then((data) => { + setCurrentEmployee(data.data); + }) + } + }, []); + + return ( + <> +
+
+
{currentEmployee?.name}
+
+
+
+
+
Username: {currentEmployee?.username}
+
+
+
Name: {currentEmployee?.name}
+
+
+
Preferred Name (中文姓名): {currentEmployee?.name_cn}
+
+
+
Birth Date: {currentEmployee?.birth_date}
+
+
+
Roles: {currentEmployee?.roles?.join(', ')}
+
+
+
Email: {currentEmployee?.email}
+
+
+
Driver Capacity: {currentEmployee?.driver_capacity}
+
+
+
Phone: {currentEmployee?.phone}
+
+
+
Mobile Phone: {currentEmployee?.mobile_phone}
+
+
+
Home Phone: {currentEmployee?.home_phone}
+
+
+
Language: {currentEmployee?.language}
+
+
+
Employment Type: {currentEmployee?.employment_status}
+
+
+
Address: {currentEmployee?.address}
+
+
+
Title: {EMPLOYEE_TITLE[currentEmployee?.title]}
+
+
+
Title CN(中文称谓): {currentEmployee?.title_cn}
+
+
+
Department: {currentEmployee?.department}
+
+
+
Date Hired: {currentEmployee?.date_hired}
+
+
+
Note: {currentEmployee?.note}
+
+
+
Tags: {currentEmployee?.tags?.join(', ')}
+
+
+ + ); +}; + +export default ViewEmployee; \ No newline at end of file diff --git a/client/src/components/event-request/CreateEventRequest.js b/client/src/components/event-request/CreateEventRequest.js new file mode 100644 index 0000000..823cf17 --- /dev/null +++ b/client/src/components/event-request/CreateEventRequest.js @@ -0,0 +1,258 @@ +import React, {useState, useEffect} from "react"; +// import { useDispatch } from "react-redux"; +import { useNavigate, useParams } from "react-router-dom"; +// import { customerSlice } from "./../../store"; +import { AuthService, CustomerService, EventRequestsService, EventsService, ResourceService } from "../../services"; +import Select from 'react-select'; +import { Button, Modal } from "react-bootstrap"; + +const CreateEvent = () => { + const navigate = useNavigate(); + const [resources, setResources] = useState([]); + const [customers, setCustomers] = useState([]); + const [currentCustomer, setCurrentCustomer] = useState(undefined); + const [customerDisplay, setCustomerDisplay] = useState(''); + const [medicalResource, setMedicalResource] = useState(undefined) + const [currentResource, setCurrentResource] = useState(undefined); + const [resourceType, setResourceType] = useState(''); + const [source, setSource] = useState(''); + const [np, setNp] = useState(''); + const [symptom, setSymptom] = useState(''); + const [transportation, setTransportation] = useState(''); + const [showResourceModal, setShowResourceModal] = useState(false); + const [keyword, setKeyword] = useState(''); + const [filteredResources, setFilteredResources] = useState([]); + // const [upload, setUpload] = useState(''); + // const [uploadOther, setUploadOther] = useState(''); + const [type, setType] = useState(''); + + + // const params = new URLSearchParams(window.location.search); + const goToRequestList = () => { + navigate(`/medical/events/request/list`); + } + + const redirectTo = () => { + navigate(`/medical/index`); + } + + + const saveEventRequest = () => { + const newEventRequest = { + customer_id: currentCustomer?.id, + customer_display: `${currentCustomer?.name} - ${currentCustomer?.name_cn} - ${currentCustomer?.birth_date}`, + source, + symptom, + resource: currentResource?.id, + resource_display: currentResource?.name, + transportation, + np, + upload: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, + type, + status: 'active', + create_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, + create_date: new Date(), + edit_history:[{ employee: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, date: new Date() }] + } + + console.log('new Event Request', newEventRequest); + EventRequestsService.createNewEventRequest(newEventRequest).then(data => goToRequestList()); + }; + + + useEffect(() => { + if (!AuthService.canAccessLegacySystem()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + Promise.all([ResourceService.getAll(), CustomerService.getAllCustomers()]).then(([resourcesData, customersData]) => { + setResources(resourcesData.data); + setFilteredResources(resourcesData.data); + setCustomers(customersData.data); + }) + }, []); + + useEffect(() => { + const newResources = [...resources]; + + if (resourceType && resourceType !== '') { + setFilteredResources(newResources?.filter(item => item.type === resourceType)); + } + }, [resourceType]) + + const onCustomerChange = (selectedCustomer) => { + setCustomerDisplay(selectedCustomer); + setCurrentCustomer(customers.find(customer => customer.id === selectedCustomer.value)) + } + + const filterResourceListByKeyword = (item) => { + if (keyword.length > 0) { + const newKeyword = keyword.toLowerCase(); + return item?.name.toLowerCase()?.includes(newKeyword) || item?.address?.toLowerCase()?.includes(newKeyword) || item?.specialty?.toLowerCase()?.includes(newKeyword); + } else { + return true; + } + } + + const filterResourcesListBySpecialty = (item) => { + if (resourceType.length > 0) { + return item?.type === resourceType; + } else { + return true; + } + } + + // const onResourceChange = (selectedResource) => { + // setMedicalResource(selectedResource); + // setCurrentResource(resources.find(resource => resource.id === selectedResource.value)); + // } + + return ( + <> +
+
+
+
+
Create Event Request
+
+
+
+
+
Source*:
+ +
+
+
Type*:
+ +
+
+
Patient*:
+ + +
+
+
Doctor:
+ {currentResource ? (<>{currentResource?.name} ) : ()} + {/* */} +
+
+
Symptom 和特殊要求:
+
+
+
+ Transfer To Route: {setCustomerTransferToRoute(e.target.value)}}/> +
+ + + + + + + + closeGroupEditorModal()}> + + Special Edit Group Participants + + + <> + {isInbound &&
+ Estimated Pickup: +
} +
+
+ Special Checkin: {if (!customerCheckInTime || customerCheckInTime.length === 0) { setCustomerCheckInTime(new Date())}}} value={customerCheckInTime} onChange={setCustomerCheckInTime} /> +
+
+
+ Special Checkout: {if (!customerCheckOutTime || customerCheckOutTime.length === 0) { setCustomerCheckOutTime(new Date())}}} value={customerCheckOutTime} onChange={setCustomerCheckOutTime} /> +
+
+
+ Special Pickup: {if (!customerPickUpTime || customerPickUpTime.length === 0) { setCustomerPickUpTime(new Date())}}} value={customerPickUpTime} onChange={setCustomerPickUpTime} /> +
+
+
+ Special Dropoff: {if (!customerDropOffTime || customerDropOffTime.length === 0) { setCustomerDropOffTime(new Date())}}} value={customerDropOffTime} onChange={setCustomerDropOffTime} /> +
+
+
+ Special Set Users Route Status: +
+ +
+ + + + +
+ + ); +}; + +export default PersonnelInfoTable; \ No newline at end of file diff --git a/client/src/components/trans-routes/PersonnelSection.js b/client/src/components/trans-routes/PersonnelSection.js new file mode 100644 index 0000000..b861c9d --- /dev/null +++ b/client/src/components/trans-routes/PersonnelSection.js @@ -0,0 +1,18 @@ +import React from "react"; +import PersonnelInfoTable from "./PersonnelInfoTable"; + +const PersonnelSection = ({transRoutes, sectionName, showCompletedInfo, showGroupInfo, allowForceEdit, showFilter, driverName, vehicle, relatedOutbound, vehicles, isInbound, deleteFile}) => { + return ( + <> +
{sectionName}
+
+
+ +
+
+ + + ); +}; + +export default PersonnelSection; \ No newline at end of file diff --git a/client/src/components/trans-routes/RouteCard.js b/client/src/components/trans-routes/RouteCard.js new file mode 100644 index 0000000..1c5ea10 --- /dev/null +++ b/client/src/components/trans-routes/RouteCard.js @@ -0,0 +1,119 @@ +import React from "react"; +import { useNavigate} from "react-router-dom"; +import { ROUTE_STATUS, PICKUP_STATUS } from "./../../shared"; + +const RouteCard = ({transRoute, drivers, vehicles, driver, vehicle}) => { + const navigate = useNavigate(); + const params = new URLSearchParams(window.location.search); + const scheduleDate = params.get('dateSchedule'); + const handleOnClick = () => { + if (scheduleDate) { + navigate(`/trans-routes/${transRoute.id}?dateSchedule=${scheduleDate}`); + } else { + navigate(`/trans-routes/${transRoute.id}`); + } + + } + const goToEditDriver = (id) => { + navigate(`/employees/edit/${id}?redirect=schedule&type=driver`); + } + const goToEditVehicle = (id) => { + navigate(`/vehicles/edit/${id}?redirect=schedule`); + } + const getRouteStatus = (route) => { + let routeStatusData = {}; + const hasAbsentScheduled = route?.route_customer_list?.find((customer) => customer.customer_pickup_status === PICKUP_STATUS.SCHEDULE_ABSENT); + const unexpectedAbsent = route?.status?.includes(ROUTE_STATUS.UNEXPECTED_ABSENT); + if (route?.status?.includes(ROUTE_STATUS.ENROUTE)) { + routeStatusData = { + text: 'Enroute', + className: 'yellow' + } + } + if (route?.status?.includes(ROUTE_STATUS.ENROUTE_TO_CENTER)) { + routeStatusData = { + text: 'Picked up all participants, enroute to center', + className: 'blue' + } + if (hasAbsentScheduled || unexpectedAbsent) { + routeStatusData = { + text: 'Has absents, enroute to center', + className: 'blue' + } + } + } + if (route?.status?.includes(ROUTE_STATUS.DROPPED_OFF_ALL)) { + routeStatusData = { + text: 'Dropped off all participants', + className: 'blue' + } + } + if (route?.status?.includes(ROUTE_STATUS.SIGN_OFF)) { + routeStatusData = { + text: route.type === 'inbound' ? 'Backed to center' : 'Task finished, vehicle parked', + className: 'green' + } + } + if (!route?.status || route?.status?.length === 0 || route?.status?.includes(ROUTE_STATUS.NOT_STARTED)) { + routeStatusData = { + text: 'Not Started', + className: 'gray' + } + } + const now = new Date(); + const dateString = ((now.getMonth() > 8) ? (now.getMonth() + 1) : ('0' + (now.getMonth() + 1))) + '/' + ((now.getDate() > 9) ? now.getDate() : ('0' + now.getDate())) + '/' + now.getFullYear(); + // if (!route?.status?.includes(ROUTE_STATUS.SIGN_OFF) && now.getHours() >= 16 && dateString === route?.schedule_date) { + if (!route?.status?.includes(ROUTE_STATUS.SIGN_OFF) && Date.parse(dateString) > Date.parse(route?.schedule_date)) { + routeStatusData = { + text: 'After 4pm, not sign off', + className: 'purple' + } + } + return routeStatusData; + } + + const routeStatus = getRouteStatus(transRoute); + + return ( + <> + { + transRoute && (
+
{transRoute.name}
+
{`${transRoute?.route_customer_list?.length} Participants`}
+
{drivers.find((driver) => driver.id === transRoute.driver)?.name}
+
{vehicles.find((vehicle) => vehicle.id === transRoute.vehicle)?.tag}
+
+
+
{routeStatus?.text}
+
+
+
+
) + } + { + !transRoute && driver && (
goToEditDriver(driver.id)}> +
{driver.name}
+
+
+
{`Capacity Limit: ${driver.driver_capacity} seats`}
+
+
+
) + } + { + !transRoute && vehicle && (
goToEditVehicle(vehicle.id)}> +
{vehicle.vehicle_number}
+
Tag: {vehicle.tag}
+
{`Capacity Limit: ${vehicle.capacity} seats`}
+
EzPass: {vehicle.ezpass}
+
GPS Tag: {vehicle.gps_tag}
+
) + } + + + + + ); +}; + +export default RouteCard; \ No newline at end of file diff --git a/client/src/components/trans-routes/RouteCustomerEditor.js b/client/src/components/trans-routes/RouteCustomerEditor.js new file mode 100644 index 0000000..31974df --- /dev/null +++ b/client/src/components/trans-routes/RouteCustomerEditor.js @@ -0,0 +1,626 @@ +import React, {useEffect, useRef, useState, useCallback} from 'react'; +import { useDrag, useDrop, DndProvider } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; +import update from 'immutability-helper'; +import { Modal, Button } from "react-bootstrap"; +import { CustomerService } from '../../services'; +import { PERSONAL_ROUTE_STATUS } from '../../shared'; +import ReactPaginate from 'react-paginate'; + + +const ItemTypes = { + CARD: 'card', +}; + +const Card = ({ content, index, moveCard }) => { + const ref = useRef(null); + const [{ handlerId }, drop] = useDrop({ + accept: ItemTypes.CARD, + collect(monitor) { + return { + handlerId: monitor.getHandlerId(), + } + }, + drop(item, monitor) { + if (!ref.current) { + return + } + const dragIndex = item.index + const hoverIndex = index + // Don't replace items with themselves + if (dragIndex === hoverIndex) { + return + } + // Determine rectangle on screen + const hoverBoundingRect = ref.current?.getBoundingClientRect() + // Get vertical middle + const hoverMiddleY = + (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2 + // Determine mouse position + const clientOffset = monitor.getClientOffset() + // Get pixels to the top + const hoverClientY = clientOffset.y - hoverBoundingRect.top + // Only perform the move when the mouse has crossed half of the items height + // When dragging downwards, only move when the cursor is below 50% + // When dragging upwards, only move when the cursor is above 50% + // Dragging downwards + if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) { + return + } + // Dragging upwards + if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) { + return + } + // Time to actually perform the action + moveCard(dragIndex, hoverIndex) + // Note: we're mutating the monitor item here! + // Generally it's better to avoid mutations, + // but it's good here for the sake of performance + // to avoid expensive index searches. + item.index = hoverIndex + }, + }) + const [{ isDragging }, drag] = useDrag({ + type: ItemTypes.CARD, + item: () => { + return { index } + }, + collect: (monitor) => ({ + isDragging: monitor.isDragging(), + }), + }) + const opacity = isDragging ? 0 : 1 + drag(drop(ref)) + return ( +
+ {content} +
+ ) +} + +const RouteCustomerEditor = ({currentRoute, setNewCustomerList}) => { + const [customers, setCustomers] = useState([]); + const [showAddPersonnelModal, setShowAddPersonnelModal] = useState(false); + const [showAddAptGroupModal, setShowAddAptGroupModal] = useState(false); + const [showEditAptGroupModal, setShowEditAptGroupModal] = useState(false); + const [editGroupIndex, setEditGroupIndex] = useState(-1); + const [customerOptions, setCustomerOptions] = useState([]); + const [customerFilter, setCustomerFilter] = useState(''); + const [lastNameFilter, setLastNameFilter] = useState(undefined); + const [newRouteCustomerList, setNewRouteCustomerList] = useState([]); + const [newRouteGroupedCustomerList, setNewRouteGroupedCustomerList] = useState([]); + const [newGroupName, setNewGroupName] = useState(''); + const [newGroupAddress, setNewGroupAddress] = useState(''); + + // We start with an empty list of items. + const [currentItems, setCurrentItems] = useState(null); + // Here we use item offsets; we could also use page offsets + // following the API or data you're working with. + const [itemOffset, setItemOffset] = useState(0); + const [pageCount, setPageCount] = useState(0); + const itemsPerPage = 10; + + + useEffect(() => { + // Fetch items from another resources. + const endOffset = itemOffset + itemsPerPage; + setCurrentItems(customerOptions?.filter(customer => (lastNameFilter && (customer.lastname?.toLowerCase().indexOf(lastNameFilter) === 0)) || !lastNameFilter).filter((customer) => customer.name?.toLowerCase().includes(customerFilter?.toLowerCase()) || customer.id.toLowerCase().includes(customerFilter?.toLowerCase()) || customer.address1?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address2?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address3?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address4?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address5?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.apartment?.toLowerCase().includes(customerFilter.toLocaleLowerCase()) ).slice(itemOffset, endOffset)); + setPageCount(Math.ceil(customerOptions?.filter(customer => (lastNameFilter && (customer.lastname?.toLowerCase().indexOf(lastNameFilter) === 0)) || !lastNameFilter).filter((customer) => customer.name.toLowerCase().includes(customerFilter?.toLowerCase()) || customer.id.toLowerCase().includes(customerFilter?.toLowerCase()) || customer.address1?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address2?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address3?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address4?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address5?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.apartment?.toLowerCase().includes(customerFilter.toLocaleLowerCase()) ).length / itemsPerPage)); + }, [customerOptions, itemOffset, customerFilter, lastNameFilter]); + + const handlePageClick = (event) => { + const newOffset = (event.selected * itemsPerPage) % customerOptions?.filter((customer) => customer.name?.toLowerCase().includes(customerFilter?.toLowerCase()) || customer.id?.toLowerCase().includes(customerFilter?.toLowerCase()) || customer.address1?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address2?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address3?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address4?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address5?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.apartment?.toLowerCase().includes(customerFilter.toLocaleLowerCase()) ).length; + console.log( + `User requested page number ${event.selected}, which is offset ${newOffset}` + ); + setItemOffset(newOffset); + }; + + const closeAddPersonnelModal = () => { + setShowAddPersonnelModal(false); + setNewRouteCustomerList([]); + } + + const openAddPersonnelModal = () => { + setItemOffset(0); + + setPageCount(0); + setLastNameFilter(undefined); + if (customerOptions.length === 0) { + CustomerService.getAllActiveCustomers().then((data) => { + setCustomerOptions(data.data); + }) + } + setShowAddPersonnelModal(true); + } + + const closeAddAptGroupModal = () => { + setShowAddAptGroupModal(false); + } + + const openAddAptGroupModal = () => { + setItemOffset(0); + + setPageCount(0); + setLastNameFilter(undefined); + if (customerOptions.length === 0) { + CustomerService.getAllActiveCustomers().then((data) => { + setCustomerOptions(data.data); + }) + } + setShowAddAptGroupModal(true); + } + + const closeEditAptGroupModal = () => { + setShowEditAptGroupModal(false); + setNewGroupAddress(''); + setNewGroupName(''); + setNewRouteGroupedCustomerList([]); + setEditGroupIndex(-1); + } + + const openEditAptGroupModal = (index, group) => { + setItemOffset(0); + + setPageCount(0); + setLastNameFilter(undefined); + if (customerOptions.length === 0) { + CustomerService.getAllActiveCustomers().then((data) => { + setCustomerOptions(data.data); + }) + } + setNewGroupAddress(group.customers[0].customer_group_address); + setNewGroupName(group.customer_group); + setNewRouteGroupedCustomerList(group.customers); + setEditGroupIndex(index); + setShowEditAptGroupModal(true); + } + + const toggleItemToRouteList = (customer, value) => { + if (value === 'false') { + setNewRouteCustomerList([].concat(newRouteCustomerList).concat([{ + customer_id: customer.id, + customer_name: `${customer.name} ${customer.name_cn?.length > 0 ? `(${customer.name_cn})` : ``}`, + customer_address: customer.address1, + customer_avatar: customer.avatar, + customer_type: customer.type, + customer_pickup_status: customer.pickup_status, + customer_note: customer.note, + customer_special_needs: customer.special_needs, + customer_phone: customer.phone || customer.mobile_phone || customer.home_phone, + customer_route_status: PERSONAL_ROUTE_STATUS.NO_STATUS, + customer_pickup_order: customers.length + newRouteCustomerList.length + 1, + customer_table_id: customer.table_id, + customer_language: customer.language + }])); + } else { + setNewRouteCustomerList([].concat(newRouteCustomerList.filter((item) => item.customer_id !== customer.id))); + } + } + + const toggleGroupedItemToRouteList = (customer, value) => { + if (value === 'false') { + setNewRouteGroupedCustomerList([].concat(newRouteGroupedCustomerList).concat([{ + customer_id: customer.id, + customer_name: `${customer.name} ${customer.name_cn?.length > 0 ? `(${customer.name_cn})` : ``}`, + customer_address: customer.address1, + customer_avatar: customer.avatar, + customer_group: newGroupName, + customer_group_address: newGroupAddress, + customer_type: customer.type, + customer_pickup_status: customer.pickup_status, + customer_note: customer.note, + customer_special_needs: customer.special_needs, + customer_phone: customer.phone || customer.mobile_phone || customer.home_phone, + customer_route_status: PERSONAL_ROUTE_STATUS.NO_STATUS, + customer_pickup_order: customers.length + 1, + customer_table_id: customer.table_id, + customer_language: customer.language + }])); + } else { + setNewRouteGroupedCustomerList([].concat(newRouteGroupedCustomerList.filter((item) => item.customer_id !== customer.id))); + } + } + + + const setCustomerAddress = (id, value) => { + setNewRouteCustomerList(newRouteCustomerList.map((item) => { + if (item.customer_id === id) { + return { + ...item, + customer_address: value + } + } else { + return item; + } + })) + } + + const setGroupedCustomerAddress = (id, value) => { + setNewRouteGroupedCustomerList(newRouteGroupedCustomerList.map((item) => { + if (item.customer_id === id) { + return { + ...item, + customer_address: value + } + } else { + return item; + } + })) + } + + const setNewGroupNameAction = (value) => { + setNewGroupName(value); + for (const item of newRouteGroupedCustomerList) { + item.customer_group = value; + } + } + + const setNewGroupAddressAction = (value) => { + setNewGroupAddress(value); + for (const item of newRouteGroupedCustomerList) { + item.customer_group_address = value; + } + } + + const checkGroupRequiredField = () => { + if ((!newGroupName || newGroupName.replace(' ', '') === '') || (!newGroupAddress || newGroupAddress.replace(' ', '') === '')) { + window.alert('Group Name and Group Address is Required') + return false; + } + return true; + } + + const addPersonnel = () => { + const result = [].concat(customers).concat(newRouteCustomerList); + setCustomers(result.filter((item, pos) => result.indexOf(item) === pos)); + setShowAddPersonnelModal(false); + setNewRouteCustomerList([]); + } + + const addAptGroup = () => { + if (checkGroupRequiredField()) { + const result = [].concat(customers).concat([{ + customers: newRouteGroupedCustomerList, + customer_pickup_order: customers.length + 1, + customer_group: newGroupName, + }]); + setCustomers(result.filter((item, pos) => result.indexOf(item) === pos)); + setShowAddAptGroupModal(false); + setNewRouteGroupedCustomerList([]); + setNewGroupAddress(''); + setNewGroupName(''); + setEditGroupIndex(-1); + } + } + + const editAptGroup = () => { + if (checkGroupRequiredField()) { + const result = [].concat(customers); + result[editGroupIndex] = { + ...result[editGroupIndex], + customers: newRouteGroupedCustomerList, + customer_group: newGroupName, + } + setCustomers(result.filter((item, pos) => result.indexOf(item) === pos)); + setShowEditAptGroupModal(false); + setNewGroupAddress(''); + setNewGroupName(''); + setNewRouteGroupedCustomerList([]); + setEditGroupIndex(-1); + } + } + + useEffect(() => { + setCustomers(getRouteCustomersWithGroups()); + }, [currentRoute]) + const getRouteCustomersWithGroups = () => { + const customerList = currentRoute?.route_customer_list?.map(item => Object.assign({}, item, {routeType: currentRoute.type, routeId: currentRoute.id})); + const result = {}; + if (customerList) { + for (const customer of customerList) { + if (customer.customer_group) { + if (result[customer.customer_group]) { + result[customer.customer_group].push(customer); + } else { + result[customer.customer_group] = []; + result[customer.customer_group].push(customer); + } + } else { + if (result.no_group) { + result.no_group.push(customer); + } else { + result.no_group = []; + result.no_group.push(customer); + } + } + } + } + let finalResult = []; + for (const key of Object.keys(result)) { + if (key === 'no_group') { + finalResult = finalResult.concat(result[key]); + } else { + finalResult.push({ + customer_pickup_order: result[key][0].customer_pickup_order, + customer_group: key, + customers: result[key] + }) + } + } + return finalResult.sort((a, b) => a.customer_pickup_order - b.customer_pickup_order); + } + + const deleteCustomer = (id) => { + setCustomers(customers.filter((customer) => customer.customer_id !== id)); + } + + const deleteGroup = (index) => { + const arr = [].concat(customers); + arr.splice(index, 1); + setCustomers(arr); + } + + const reorderItems = useCallback((dragIndex, hoverIndex) => { + setCustomers((prevCards) => { + return update(prevCards, { + $splice: [ + [dragIndex, 1], + [hoverIndex, 0, prevCards[dragIndex]] + ] + }) + }); + + }, []); + + const Items = ({ currentItems }) => { + return currentItems?.map( + (customer) =>
+ item.customer_id === customer.id)!==undefined} value={newRouteCustomerList.find((item) => item.customer_id === customer.id)!==undefined} onChange={(e) => toggleItemToRouteList(customer, e.target.value)}/> +
+
{`${customer.name}(${customer.name_cn})`}
+ {newRouteCustomerList.find((item) => item.customer_id === customer.id) && (
+ {customer.address1 && customer.address1 !== '' &&
setCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address1} checked={newRouteCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address1}/>{customer.address1}
} + {customer.address2 && customer.address2 !== '' &&
setCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address2} checked={newRouteCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address2}/>{customer.address2}
} + {customer.address3 && customer.address3 !== '' &&
setCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address3} checked={newRouteCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address3}/>{customer.address3}
} + {customer.address4 && customer.address4 !== '' &&
setCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address4} checked={newRouteCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address4}/>{customer.address4}
} + {customer.address5 && customer.address5 !== '' &&
setCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address5} checked={newRouteCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address5}/>{customer.address5}
} +
)} +
+
+ ) + }; + + const ItemsGroup = ({ currentItems }) => { + return currentItems?.filter((customer) => customer.name.toLowerCase().includes(customerFilter.toLowerCase()) || customer.id.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address1?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address2?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address3?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address4?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.address5?.toLowerCase().includes(customerFilter.toLowerCase()) || customer.apartment?.toLowerCase().includes(customerFilter.toLocaleLowerCase()) ).map( + (customer) =>
+ item.customer_id === customer.id)!==undefined} value={newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id)!==undefined} onChange={(e) => toggleGroupedItemToRouteList(customer, e.target.value)}/> +
+
{`${customer.name}(${customer.name_cn})`}
+ {newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id) && (
+ {customer.address1 && customer.address1 !== '' &&
setGroupedCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address1} checked={newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address1}/>{customer.address1}
} + {customer.address2 && customer.address2 !== '' &&
setGroupedCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address2} checked={newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address2}/>{customer.address2}
} + {customer.address3 && customer.address3 !== '' &&
setGroupedCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address3} checked={newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address3}/>{customer.address3}
} + {customer.address4 && customer.address4 !== '' &&
setGroupedCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address4} checked={newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address4}/>{customer.address4}
} + {customer.address5 && customer.address5 !== '' &&
setGroupedCustomerAddress(customer.id, e.currentTarget.value)} value={customer.address5} checked={newRouteGroupedCustomerList.find((item) => item.customer_id === customer.id)?.customer_address===customer.address5}/>{customer.address5}
} +
)} +
+
+ ) + } + + useEffect(() => { + const result = []; + for (const item of customers) { + if (item.customer_group) { + for (const customer of item.customers) { + customer.customer_pickup_order = customers.indexOf(item); + result.push(customer); + } + } else { + item.customer_pickup_order = customers.indexOf(item); + result.push(item); + } + } + setNewCustomerList(result); + }, [customers]) + + return ( + +
Personnel List
+
+
+ + +
+
+
+ {customers.map((item, index) => { + if (item?.customers) { + return + +
openEditAptGroupModal(index, item)}> + {item.customer_group} {item.customers[0]?.customer_group_address} +
{item.customers.map(customer => +
+ {customer.customer_name} + {customer.customer_address} + {customer.customer_pickup_status} +
)} +
+
+
+
)}> + } else { + return + +
+ {item.customer_name} + {item.customer_address} + {item.customer_pickup_status} +
+
+
}> + + + } + })} +
+ closeAddPersonnelModal()}> + + Add Personnel + + + <> +
Type in UserId OR Name OR Address to Search:
+ setCustomerFilter(e.target.value)}/> +
+ {['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'].map(item => { + return {setLastNameFilter(item?.toLowerCase())} }>{item} + })} + +
+ setLastNameFilter(undefined)}>Clear All +
+ + +
+ + + + + + + + + closeAddAptGroupModal()}> + + Add Apt Group + + + <> +
Group Name(Required): setNewGroupNameAction(e.target.value)}/>
+
Group Address(Required): setNewGroupAddressAction(e.target.value)}/>
+
Type in user Id or Name to Search:
+ setCustomerFilter(e.target.value)}/> +
+ {['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'].map(item => { + return {setLastNameFilter(item?.toLowerCase())} }>{item} + })} + +
+ setLastNameFilter(undefined)}>Clear All +
+ + +
+ +
+ + + + +
+ + closeEditAptGroupModal()}> + + Update Apt Group + + + <> +
Group Name(Required): setNewGroupNameAction(e.target.value)}/>
+
Group Address(Required): setNewGroupAddressAction(e.target.value)}/>
+
Type in user Id or Name to Search:
+ setCustomerFilter(e.target.value)}/> +
+ {['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'].map(item => { + return {setLastNameFilter(item?.toLowerCase())} }>{item} + })} + +
+ setLastNameFilter(undefined)}>Clear All +
+ + +
+ +
+ + + + +
+ + ); +}; + +export default RouteCustomerEditor; \ No newline at end of file diff --git a/client/src/components/trans-routes/RouteCustomerTable.js b/client/src/components/trans-routes/RouteCustomerTable.js new file mode 100644 index 0000000..d308582 --- /dev/null +++ b/client/src/components/trans-routes/RouteCustomerTable.js @@ -0,0 +1,67 @@ +import React, { useEffect, useState } from "react"; +import { TransRoutesService } from "../../services"; +import { CSVLink } from "react-csv"; +import moment from 'moment'; + +const RouteCustomerTable = ({transRoutes, sectionName, vehicles}) => { + + const [customers, setCustomers] = useState([]); + + useEffect(() => { + setCustomers(TransRoutesService.getAllCustomersFromRoutes([...transRoutes], vehicles)); + }) + + const generateCustomerTableVechileReportData = () => { + const head = ['No.', 'Name', 'Table', 'Vehicle Number']; + const chineseHead=['序号', '姓名', '桌号', '车号']; + const content = []; + let index = 1; + for (let i=0; i +
{sectionName}
+
+
+ + Generate Customer Table Vehicle Report + +
+
+
+
+ + + + + + + + + + { + customers?.map((customer) => ( + + + + + + )) + } + +
NameTableVehicle No.
{customer?.customer_name}{customer?.customer_table_id} + {customer?.vehicle_number} +
+
+
+ + + ); +}; + +export default RouteCustomerTable; \ No newline at end of file diff --git a/client/src/components/trans-routes/RouteEdit.js b/client/src/components/trans-routes/RouteEdit.js new file mode 100644 index 0000000..972c3da --- /dev/null +++ b/client/src/components/trans-routes/RouteEdit.js @@ -0,0 +1,297 @@ +import React, {useEffect, useState} from "react"; +import { useSelector,useDispatch } from "react-redux"; +import { useParams, useNavigate } from "react-router-dom"; +import { selectAllRoutes, transRoutesSlice, vehicleSlice, selectTomorrowAllRoutes, selectAllActiveDrivers, selectAllActiveVehicles, selectHistoryRoutes } from "./../../store"; +import { Modal, Button } from "react-bootstrap"; +import RouteCustomerEditor from "./RouteCustomerEditor"; +import { AuthService, TransRoutesService } from "../../services"; +import TimePicker from 'react-time-picker'; +import 'react-time-picker/dist/TimePicker.css'; +import moment from 'moment'; + +const RouteEdit = () => { + const params = useParams(); + const allRoutes = useSelector(selectAllRoutes); + const tomorrowRoutes = useSelector(selectTomorrowAllRoutes); + const historyRoutes = useSelector(selectHistoryRoutes); + const drivers = useSelector(selectAllActiveDrivers); + const vehicles = useSelector(selectAllActiveVehicles); + // const currentRoute = (allRoutes.find(item => item.id === params.id)) || (tomorrowRoutes.find(item => item.id === params.id)) || (historyRoutes.find(item => item.id === params.id)) || {}; + const currentVehicle = vehicles.find(item => item.id === currentRoute?.vehicle ) || []; + const navigate = useNavigate(); + const dispatch = useDispatch(); + const { updateRoute} = transRoutesSlice.actions; + const { updateVehicle} = vehicleSlice.actions; + const [routeName, setRouteName] = useState(''); + const [newDriver, setNewDriver] = useState(''); + const [newVehicle, setNewVehicle] = useState(''); + const [newRouteType, setNewRouteType] = useState(''); + const [showAddCheckItem, setShowAddCheckItem] = useState(false); + const [showCopyCheckItem, setShowCopyCheckItem] = useState(false); + const [newChecklistItems, setNewChecklistItems] = useState([]); + const [selectedRouteChecklistToCopy, setSelectedRouteChecklistToCopy] = useState({}); + const [newCustomerList, setNewCustomerList] = useState([]); + const [errorMessage, setErrorMessage] = useState(undefined); + const [estimatedStartTime, setEstimatedStartTime] = useState(undefined); + const [currentRoute, setCurrentRoute] = useState(undefined); + const paramsQuery = new URLSearchParams(window.location.search); + const scheduleDate = paramsQuery.get('dateSchedule'); + const redirectToView = () => { + if (scheduleDate) { + navigate(`/trans-routes/${params.id}?dateSchedule=${scheduleDate}`); + } else { + navigate(`/trans-routes/${params.id}`); + } + } + + const redirectToDashboard = () => { + navigate(`/trans-routes/dashboard`); + } + + const softDeleteCurrentRoute = () => { + const data = Object.assign({}, currentRoute, {status: ['disabled']}) + dispatch(updateRoute({ id: currentRoute?.id, data, callback: redirectToDashboard })); + // redirectToDashboard(); + } + + const updateCurrentRoute = () => { + try { + if (!routeName || routeName === '') { setErrorMessage('Route Name is Required'); return;} + if (!newRouteType || newRouteType === '') {setErrorMessage('Route Type is Required'); return;} + if (!newDriver || newDriver === '') {setErrorMessage('Driver is Required'); return;} + if (!newVehicle || newVehicle === '') {setErrorMessage('Vehicle is Required'); return;} + let data = Object.assign({}, currentRoute, {name: routeName, driver: newDriver, vehicle: newVehicle, type: newRouteType, route_customer_list: newCustomerList}); + if (estimatedStartTime && estimatedStartTime !== '') { + data = Object.assign({}, data, {estimated_start_time: combineDateAndTime(currentRoute.schedule_date, estimatedStartTime)}) + } + let payload = { id: currentRoute?.id, data }; + if ((historyRoutes.find(item => item.id === params.id)) || (scheduleDate && new Date(data.schedule_date) > new Date())) { + payload = Object.assign({}, payload, {dateText: data.schedule_date}); + if (scheduleDate && new Date(data.schedule_date) > new Date()) { + payload = Object.assign({}, payload, {fromSchedule: true}); + } + } + payload.callback = redirectToView; + dispatch(updateRoute(payload)); + // TransRoutesService.updateInProgress(data); + // setTimeout(() => { + // redirectToView(); + // }, 5000); + } catch(ex) { + + } + } + + const addItemToArray = () => { + const arr = [...newChecklistItems, '']; + setNewChecklistItems(arr); + } + + const saveChecklistItems = () => { + const data = Object.assign({}, currentVehicle, {checklist: newChecklistItems}); + dispatch(updateVehicle({ id: currentVehicle.id, data })); + setShowAddCheckItem(false); + } + + const copyChecklistItems = () => { + const data = Object.assign({}, currentVehicle, {checklist: vehicles.find(vehicle => vehicle.id === selectedRouteChecklistToCopy.vehicle)?.checklist}); + dispatch(updateVehicle({ id: currentVehicle.id, data })); + setShowCopyCheckItem(false); + } + + const closeAddCheckItemModal = () => { + setNewChecklistItems([]) + setShowAddCheckItem(false); + } + + const showAddCheckItemModal = () => { + setNewChecklistItems(currentVehicle.checklist) + setShowAddCheckItem(true); + } + + const closeCopyCheckItemModal = () => { + setSelectedRouteChecklistToCopy({}); + setShowCopyCheckItem(false); + } + + const showCopyCheckItemModal = () => { + setShowCopyCheckItem(true); + } + + const combineDateAndTime = (date, time) => { + const dateObj = moment(date); + const timeObj = moment(time, 'HH:mm'); + dateObj.set({ + hour: timeObj.get('hour'), + minute: timeObj.get('minute'), + second: timeObj.get('second') + }) + return dateObj; + } + + useEffect(() => { + if (!AuthService.canAddOrEditRoutes()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + TransRoutesService.getRoute(params.id).then(data => { + setCurrentRoute(data?.data); + setRouteName(data?.data?.name); + setNewDriver(data?.data?.driver); + setNewVehicle(data?.data?.vehicle); + setNewRouteType(data?.data?.type); + setEstimatedStartTime(data?.data?.estimated_start_time && new Date(data?.data?.estimated_start_time)); + setNewCustomerList(data?.data?.route_customer_list); + setErrorMessage(undefined); + }) + }, []); + + // useEffect(() => { + // if (currentRoute) { + // setRouteName(currentRoute.name); + // setNewDriver(currentRoute.driver); + // setNewVehicle(currentRoute.vehicle); + // setNewRouteType(currentRoute.type); + // setEstimatedStartTime(currentRoute.estimated_start_time && new Date(currentRoute.estimated_start_time)); + // setNewCustomerList(currentRoute.route_customer_list); + // } + // setErrorMessage(undefined); + // }, [currentRoute]) + + return ( + <> +
+
+
{currentRoute?.name}
+
+
+
+
+ + + +
+ {errorMessage &&
{errorMessage}
} +
+
+
+ Name(*): setRouteName(e.target.value)}/> +
+
Vehicle(*): +
+
Driver(*): +
+
Type(*): +
+ { + newRouteType === 'outbound' && (
+ Estimated Start TIme: +
) + } +
+
+
+ +
+
+ { newVehicle && newVehicle !== '' && (
+
+
Vehicle Info
+
+
Vehicle Number: {vehicles.find(item => item.id === newVehicle)?.vehicle_number}
+
Tag: {vehicles.find(item => item.id === newVehicle)?.tag}
+
EzPass: {vehicles.find(item => item.id === newVehicle)?.ezpass}
+
GPS: {vehicles.find(item => item.id === newVehicle)?.gps_tag}
+
Capacity: {vehicles.find(item => item.id === newVehicle)?.capacity}
+
Status: {vehicles.find(item => item.id === newVehicle)?.status}
+
Mileage: {vehicles.find(item => item.id === newVehicle)?.mileage}
+
+
+
)} + { newDriver && newDriver !== '' && (
+
+
Driver Info
+
+
Name: {drivers.find(item => item.id === newDriver)?.name}
+
Preferred Name: {drivers.find(item => item.id === newDriver)?.name_cn}
+
Driver Capacity: {drivers.find(item => item.id === newDriver)?.driver_capacity}
+
Roles: {drivers.find(item => item.id === newDriver)?.roles}
+
Phone: {drivers.find(item => item.id === newDriver)?.phone}
+
Email: {drivers.find(item => item.id === newDriver)?.email}
+
Employment Status: {drivers.find(item => item.id === newDriver)?.employment_status}
+
+
+
)} +
+
+
Vehicle Checklist
+ { currentVehicle?.checklist?.length > 0 && ( + + {currentVehicle.checklist.map((item, index) => ())} + +
{item}
) } +
+
+
+ +
+ closeAddCheckItemModal()}> + + Add New Checklist Item + + + <> + {newChecklistItems.map((item, index) => (
setNewChecklistItems([...newChecklistItems].map((a, index1) => {if (index1 === index) {return e.target.value;} return a;}))}/> + +
))} + + +
+ + + + +
+ closeCopyCheckItemModal()}> + + Click on Route to Select + + + <> + {[...allRoutes, ...tomorrowRoutes].filter(r => r.id !== currentRoute?.id).map((route) => { + return (
setSelectedRouteChecklistToCopy(route)}> +
{route.name}
+
+ {vehicles.find((a) => a.id === route.vehicle)?.checklist?.map((item, index) => {item})} +
+
); + })} + +
+ + + + +
+ + + ); +}; + +export default RouteEdit; \ No newline at end of file diff --git a/client/src/components/trans-routes/RouteReportWithSignature.js b/client/src/components/trans-routes/RouteReportWithSignature.js new file mode 100644 index 0000000..0cd6362 --- /dev/null +++ b/client/src/components/trans-routes/RouteReportWithSignature.js @@ -0,0 +1,210 @@ +import React, {useState, useEffect} from "react"; +import { useSelector } from "react-redux"; +import { useParams, useNavigate } from "react-router-dom"; +import { selectAllRoutes, selectTomorrowAllRoutes, selectAllActiveDrivers, selectAllActiveVehicles, selectHistoryRoutes } from "../../store"; +import { CustomerService, SignatureRequestService, EventsService } from "../../services"; +import moment from 'moment'; +import { CUSTOMER_TYPE, CUSTOMER_TYPE_TEXT, PERSONAL_ROUTE_STATUS } from "../../shared"; + +const RouteReportWithSignature = () => { + const params = useParams(); + const allRoutes = useSelector(selectAllRoutes); + const tomorrowRoutes = useSelector(selectTomorrowAllRoutes); + const historyRoutes = useSelector(selectHistoryRoutes); + const drivers = useSelector(selectAllActiveDrivers); + const vehicles = useSelector(selectAllActiveVehicles); + const currentRoute = (allRoutes.find(item => item.id === params.id)) || (tomorrowRoutes.find(item => item.id === params.id)) || (historyRoutes.find(item => item.id === params.id)); + const routeIndex = currentRoute?.type === 'inbound' ? + ((allRoutes.map((r) => Object.assign({}, r, {vehicleNumber: vehicles?.find((v) => r.vehicle === v.id)?.vehicle_number || 0})).sort((a, b) => a?.vehicleNumber - b?.vehicleNumber).filter(item => item.type === 'inbound').findIndex(item => item.id === params.id))) + 1 || ((tomorrowRoutes.filter(item => item.type === 'inbound').findIndex(item => item.id === params.id)))+1 || ((historyRoutes.filter(item => item.type === 'inbound').findIndex(item => item.id === params.id)))+1 : + ((allRoutes.map((r) => Object.assign({}, r, {vehicleNumber: vehicles?.find((v) => r.vehicle === v.id)?.vehicle_number || 0})).sort((a, b) => a?.vehicleNumber - b?.vehicleNumber).filter(item => item.type === 'outbound').findIndex(item => item.id === params.id)))+1 || ((tomorrowRoutes.filter(item => item.type === 'outbound').findIndex(item => item.id === params.id))) +1 || ((historyRoutes.filter(item => item.type === 'outbound').findIndex(item => item.id === params.id)))+1; + const currentVehicle = vehicles.find(item => item.id === currentRoute?.vehicle ); + const currentDriver = drivers.find(item => item.id === currentRoute?.driver); + + const [signature, setSignature] = useState(undefined); + + const [directorSignature, setDirectorSignature] = useState(undefined); + + const site = EventsService.site; + + const navigate = useNavigate(); + + const getRelatedInboundOutboundRoutesForThisView = (routeType) => { + if (allRoutes.find(item => item.id === params.id)) { + return allRoutes.filter(item => item.type!== routeType); + } + if (tomorrowRoutes.find(item => item.id === params.id)) { + return tomorrowRoutes.filter(item => item.type!==routeType); + } + if (historyRoutes.find(item => item.id === params.id)) { + return historyRoutes.filter(item => item.type!==routeType); + } + } + + const getInboundOrOutboundRouteCustomer = (customer_id) => { + return getRelatedInboundOutboundRoutesForThisView(currentRoute?.type)?. + find((route) => route?.name?.toLowerCase()?.replaceAll(' ', '') === currentRoute?.name?.toLowerCase()?.replaceAll(' ', '') && route?.schedule_date == currentRoute?.schedule_date)?.route_customer_list?. + find((cust) => cust?.customer_id === customer_id) + } + + const getOtherRouteWithThisCustomer = (customer_id) => { + return getRelatedInboundOutboundRoutesForThisView(currentRoute?.type)?.find((route) => route?.name !== currentRoute?.name && route?.schedule_date == currentRoute?.schedule_date && route?.route_customer_list?.find(cust => cust?.customer_id === customer_id)); + } + + const getOtherOutboundRouteWithThisCustomer = (customer_id) => { + // console.log('what', getRelatedInboundOutboundRoutesForThisView('outbound')); + return getRelatedInboundOutboundRoutesForThisView('inbound')?. + find((route) => route?.schedule_date === currentRoute?.schedule_date && route?.id !== currentRoute?.id && route?.route_customer_list.find((cust) => cust?.customer_id === customer_id)); + } + + const directToView = () => { + navigate(`/trans-routes/${params.id}`) + } + + useEffect(() => { + const dateArr = moment(currentRoute?.schedule_date)?.format('MM/DD/YYYY')?.split('/') || []; + + CustomerService.getAvatar(`${currentRoute?.id}_${currentRoute?.driver}_${dateArr[0]}_${dateArr[1]}`).then(data => { + setSignature(data.data); + }); + CustomerService.getAvatar(`center_director_signature_site_${site}`).then(data => { + if (data?.data) { + setDirectorSignature(data?.data) + } + }); + }, [currentRoute]); + return ( + <> +
+
+ +
+
+
+
+ {routeIndex} +
+
+
+
+ Route (路线): {currentRoute?.name} +
+
+ Driver (司机): {currentDriver?.name} +
+
+ Vehicle (车号): {currentVehicle?.vehicle_number} +
+
+ Date (日期): {currentRoute?.schedule_date && (new Date(currentRoute?.schedule_date))?.toLocaleDateString()} +
+ +
+
+
+ Driver's Signature (司机签字): {signature && {signature && }} {currentRoute?.end_time && {new Date(currentRoute?.end_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'})}} + +
+
+ {/* Manager's Signature (经理签字): {directorSignature && {directorSignature && }} */} + Manager's Signature (经理签字): +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + currentRoute?.route_customer_list?.map((customer, index) => { + const relativeRouteCustomer = getInboundOrOutboundRouteCustomer(customer?.customer_id); + // console.log('cust', relativeRouteCustomer); + const otherRouteWithThisCustomer = getOtherRouteWithThisCustomer(customer?.customer_id); + // console.log('other', otherRouteWithThisCustomer) + const customerInOtherRoute = otherRouteWithThisCustomer?.route_customer_list?.find(cust => cust?.customer_id === customer?.customer_id); + // console.log('other user', customerInOtherRoute) + const otherOutboundRoute = getOtherOutboundRouteWithThisCustomer(customer?.customer_id); + return ( + + + + + + + + + + + + + + ) + }) + } + +
No.NamePhoneAddressRoomPick-UpPick-UpArrivalDepartureDrop-OffRestStopNotice
序号姓名联系电话地址房间号出勤接到时间抵达中心离开中心送达时间备注
YN
{index+1}{customer?.customer_name}{customer?.customer_phone}{customer?.customer_address_override || customer?.customer_address}{ ![PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT, PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT].includes(relativeRouteCustomer?.customer_route_status) && ![PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT, PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT].includes(customer?.customer_route_status) ? "✓" : ''}{![PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT, PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT].includes(relativeRouteCustomer?.customer_route_status) && ![PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT, PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT].includes(customer?.customer_route_status) ? "" : 'x'}{customer?.customer_pickup_time ? new Date(customer?.customer_pickup_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : (relativeRouteCustomer?.customer_pickup_time? new Date(relativeRouteCustomer?.customer_pickup_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : (customerInOtherRoute?.customer_pickup_time ? new Date(customerInOtherRoute?.customer_pickup_time)?.toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}): ''))}{customer?.customer_enter_center_time ? new Date(customer?.customer_enter_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : (relativeRouteCustomer?.customer_enter_center_time ? new Date(relativeRouteCustomer?.customer_enter_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : (customerInOtherRoute?.customer_enter_center_time ? new Date(customerInOtherRoute?.customer_enter_center_time)?.toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}): ''))}{customer?.customer_leave_center_time && customer?.customer_route_status !== PERSONAL_ROUTE_STATUS.SKIP_DROPOFF ? new Date(customer?.customer_leave_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : (relativeRouteCustomer?.customer_leave_center_time && relativeRouteCustomer?.customer_route_status !== PERSONAL_ROUTE_STATUS.SKIP_DROPOFF ? new Date(relativeRouteCustomer?.customer_leave_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : '')}{customer?.customer_dropoff_time && customer?.customer_route_status !== PERSONAL_ROUTE_STATUS.SKIP_DROPOFF ? new Date(customer?.customer_dropoff_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : (relativeRouteCustomer?.customer_dropoff_time && relativeRouteCustomer?.customer_route_status !== PERSONAL_ROUTE_STATUS.SKIP_DROPOFF ? new Date(relativeRouteCustomer?.customer_dropoff_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : '')} + {customer?.customer_type !== CUSTOMER_TYPE.MEMBER &&
{ CUSTOMER_TYPE_TEXT[customer?.customer_type]}
} + { !relativeRouteCustomer && otherRouteWithThisCustomer && ( +
+ {`${currentRoute?.type === 'inbound' ? 'Switch to Route ' : 'Switch from Route '} ${otherRouteWithThisCustomer?.name}`} +
+ ) + } + { + customer?.customer_route_status === PERSONAL_ROUTE_STATUS.SKIP_DROPOFF && otherOutboundRoute && ( +
+ {`Switch to Route ${otherOutboundRoute?.name}`} +
+ ) + } +
+
+
+ + ); +}; + +export default RouteReportWithSignature; \ No newline at end of file diff --git a/client/src/components/trans-routes/RouteTemplateEdit.js b/client/src/components/trans-routes/RouteTemplateEdit.js new file mode 100644 index 0000000..46ed000 --- /dev/null +++ b/client/src/components/trans-routes/RouteTemplateEdit.js @@ -0,0 +1,121 @@ +import React, {useEffect, useState} from "react"; +import { useSelector,useDispatch } from "react-redux"; +import { useNavigate, useParams } from "react-router-dom"; +import { transRouteTemplatesSlice, selectAllRouteTemplates, selectAllActiveDrivers, selectAllActiveVehicles } from "./../../store"; +import RouteCustomerEditor from "./RouteCustomerEditor"; + +const RouteTemplateEdit = () => { + const urlParams = new URLSearchParams(window.location.search); + const params = useParams(); + const drivers = useSelector(selectAllActiveDrivers); + const vehicles = useSelector(selectAllActiveVehicles); + const allTemplates = useSelector(selectAllRouteTemplates); + const currentTemplate = (allTemplates.find(item => item.id === params.id)) || {}; + const navigate = useNavigate(); + const dispatch = useDispatch(); + const { updateRouteTemplate } = transRouteTemplatesSlice.actions; + const [routeName, setRouteName] = useState(''); + const [newDriver, setNewDriver] = useState(''); + const [newVehicle, setNewVehicle] = useState(''); + const [newRouteType, setNewRouteType] = useState(''); + const [newCustomerList, setNewCustomerList] = useState([]); + + const redirectToTemplatesList = () => { + navigate(`/trans-routes/templates${urlParams.get('type')?`?type=${urlParams.get('type')}`: ''}${urlParams.get('date')? `&date=${urlParams.get('date')}` : ''}`); + } + + const softDeleteCurrentTemplate = () => { + const data = Object.assign({}, currentTemplate, {status: 'disabled'}) + dispatch(updateRouteTemplate({ id: currentTemplate.id, data, callback: redirectToTemplatesList})); + // redirectToTemplatesList(); + } + + const updateCurrentTemplate = () => { + const data = Object.assign({}, currentTemplate, {name: routeName, driver: newDriver, vehicle: newVehicle, type: newRouteType, route_customer_list: newCustomerList}); + let payload = { id: currentTemplate.id, data, callback: redirectToTemplatesList }; + dispatch(updateRouteTemplate(payload)); + // redirectToTemplatesList(); + } + + useEffect(() => { + if (currentTemplate) { + setRouteName(currentTemplate.name); + setNewDriver(currentTemplate.driver); + setNewVehicle(currentTemplate.vehicle); + setNewRouteType(currentTemplate.type); + setNewCustomerList(currentTemplate.route_customer_list); + } + }, [currentTemplate]) + + return ( + <> +
+
+
{currentTemplate?.name}
+
+
+
+
+ + + +
+ +
+
+
+ Name: setRouteName(e.target.value)}/> +
+
Vehicle: +
+
Driver: +
+
Type: +
+
+
+
+ +
+
+ { newVehicle && newVehicle !== '' && (
+
+
Vehicle Info
+
+
Vehicle Number: {vehicles.find(item => item.id === newVehicle)?.vehicle_number}
+
Tag: {vehicles.find(item => item.id === newVehicle)?.tag}
+
EzPass: {vehicles.find(item => item.id === newVehicle)?.ezpass}
+
GPS: {vehicles.find(item => item.id === newVehicle)?.gps_tag}
+
Capacity: {vehicles.find(item => item.id === newVehicle)?.capacity}
+
Status: {vehicles.find(item => item.id === newVehicle)?.status}
+
Mileage: {vehicles.find(item => item.id === newVehicle)?.mileage}
+
+
+
)} + { newDriver && newDriver !== '' && (
+
+
Driver Info
+
+
Name: {drivers.find(item => item.id === newDriver)?.name}
+
Preferred Name: {drivers.find(item => item.id === newDriver)?.name_cn}
+
Driver Capacity: {drivers.find(item => item.id === newDriver)?.driver_capacity}
+
Roles: {drivers.find(item => item.id === newDriver)?.roles}
+
Phone: {drivers.find(item => item.id === newDriver)?.phone}
+
Email: {drivers.find(item => item.id === newDriver)?.email}
+
Employment Status: {drivers.find(item => item.id === newDriver)?.employment_status}
+
+
+
)} + + + ); +}; + +export default RouteTemplateEdit; \ No newline at end of file diff --git a/client/src/components/trans-routes/RouteTemplatesList.js b/client/src/components/trans-routes/RouteTemplatesList.js new file mode 100644 index 0000000..e094d50 --- /dev/null +++ b/client/src/components/trans-routes/RouteTemplatesList.js @@ -0,0 +1,69 @@ +import React, {useState} from "react"; +import { useSelector } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import { selectAllRouteTemplates, selectAllActiveDrivers, selectAllActiveVehicles } from "./../../store"; + +const RouteTemplatesList = () => { + const params = new URLSearchParams(window.location.search); + const drivers = useSelector(selectAllActiveDrivers); + const vehicles = useSelector(selectAllActiveVehicles); + const allTemplates = useSelector(selectAllRouteTemplates); + const navigate = useNavigate(); + const [nameFilter, setNameFilter] = useState(''); + + const redirectToCreateRoute = () => { + navigate(`/trans-routes/create${params.get('type')?`?type=${params.get('type')}`: ''}${params.get('date')? `&date=${params.get('date')}` : ''}`); + } + + const goToEdit = (id) => { + navigate(`/trans-routes/templates/edit/${id}${params.get('type')?`?type=${params.get('type')}`: ''}${params.get('date')? `&date=${params.get('date')}` : ''}`); + } + + return ( + <> +
+
+
Manage Route Templates
+
+
+ +
+
+
+ Type In Template Name To Filter: setNameFilter(e.currentTarget.value)}/> +
+ + + + + + + + + + + + + { + allTemplates.filter( + (item) => item.name.includes(nameFilter)).map(({id, name, type, vehicle, driver, status}, index) => { + return ( + + + + + + + ) + }) + } + +
Template NameTypeDriverVehicleStatusAction
{name}{type}{drivers.find((d) => d.id === driver)?.name}{vehicles.find((v) => v.id === vehicle)?.vehicle_number}{status}
+
+
+ + + ); +}; + +export default RouteTemplatesList; \ No newline at end of file diff --git a/client/src/components/trans-routes/RouteView.js b/client/src/components/trans-routes/RouteView.js new file mode 100644 index 0000000..6efec86 --- /dev/null +++ b/client/src/components/trans-routes/RouteView.js @@ -0,0 +1,166 @@ +import React, {useState, useEffect} from "react"; +import { useSelector } from "react-redux"; +import { useParams, useNavigate } from "react-router-dom"; +import { selectAllRoutes, selectTomorrowAllRoutes, selectAllActiveDrivers, selectAllActiveVehicles, selectHistoryRoutes } from "./../../store"; +import PersonnelSection from "./PersonnelSection"; +import { Modal, Button } from "react-bootstrap"; +import { AuthService, CustomerService, SignatureRequestService } from "../../services"; +import moment from 'moment'; + +const RouteView = () => { + const params = useParams(); + const allRoutes = useSelector(selectAllRoutes); + const tomorrowRoutes = useSelector(selectTomorrowAllRoutes); + const historyRoutes = useSelector(selectHistoryRoutes); + const drivers = useSelector(selectAllActiveDrivers); + const vehicles = useSelector(selectAllActiveVehicles); + const currentRoute = (allRoutes.find(item => item.id === params.id)) || (tomorrowRoutes.find(item => item.id === params.id)) || (historyRoutes.find(item => item.id === params.id)); + const currentVehicle = vehicles.find(item => item.id === currentRoute?.vehicle ); + const currentDriver = drivers.find(item => item.id === currentRoute?.driver); + const [showVehicleDetails, setShowVehicleDetails] = useState(false); + const [signature, setSignature] = useState(undefined); + const [signatureRequest, setSignatureRequest] = useState(undefined); + const paramsQuery = new URLSearchParams(window.location.search); + const scheduleDate = paramsQuery.get('dateSchedule'); + + const navigate = useNavigate(); + const closeModal = () => { + setShowVehicleDetails(false); + } + const openModal = () => { + setShowVehicleDetails(true); + } + const getRelatedOutboundRoutesForThisView = () => { + if (allRoutes.find(item => item.id === params.id)) { + return allRoutes.filter(item => item.type==='outbound'); + } + if (tomorrowRoutes.find(item => item.id === params.id)) { + return tomorrowRoutes.filter(item => item.type==='outbound'); + } + if (historyRoutes.find(item => item.id === params.id)) { + return historyRoutes.filter(item => item.type==='outbound'); + } + } + const directToDashboad = () => { + if (tomorrowRoutes.find(item => item.id === params.id)) { + navigate(`/trans-routes/schedule?dateSchedule=${moment(tomorrowRoutes.find(item => item.id === params.id).schedule_date).format('YYYY-MM-DD')}`); + } else { + if (historyRoutes.find(item => item.id === params.id)) { + navigate(`/trans-routes/history`); + } else { + navigate(`/trans-routes/dashboard`); + } + } + } + const edit = () => { + if (scheduleDate) { + navigate(`/trans-routes/edit/${currentRoute?.id}?dateSchedule=${scheduleDate}`) + } else { + navigate(`/trans-routes/edit/${currentRoute?.id}`) + } + } + const deleteFile = () => { + if (signature) { + const dateArr = moment(currentRoute?.schedule_date)?.format('MM/DD/YYYY')?.split('/') || []; + CustomerService.deleteFile({name: `${currentRoute?.id}_${currentRoute?.driver}_${dateArr[0]}_${dateArr[1]}`}).then(data => { + setSignature(undefined); + }) + } + } + const generateSignatureRequest = () => { + SignatureRequestService.createNewSignatureRequest({ + driver_id: currentDriver?.id, + driver_name: currentDriver?.name, + route_id: currentRoute?.id, + route_date: currentRoute?.schedule_date, + route_name: currentRoute?.name, + status: 'active', + }).then((data) => { + setSignatureRequest(data.data); + }) + } + useEffect(() => { + const dateArr = moment(currentRoute?.schedule_date)?.format('MM/DD/YYYY')?.split('/') || []; + + CustomerService.getAvatar(`${currentRoute?.id}_${currentRoute?.driver}_${dateArr[0]}_${dateArr[1]}`).then(data => { + setSignature(data.data); + }); + SignatureRequestService.getAllSignatureRequests({driver_id: currentDriver?.id, route_id: currentRoute?.id, route_date: currentRoute?.scheduleDate}).then((data) => { + if (data?.data?.length > 0) { + setSignatureRequest(data?.data[0]); + } + }) + }, [currentRoute]); + return ( + <> +
+
+
{currentRoute?.name} {AuthService.canAddOrEditRoutes() && }
+
+
+
+
+
Vehicle Number: {currentVehicle?.vehicle_number}
+
Total Customers: {currentRoute?.route_customer_list?.length || 0}
+
Driver Name: {currentDriver?.name}
+
Route Type: { currentRoute?.type }
+ {signature &&
Driver Signature: {signature && }
} + {!signature && !signatureRequest &&
} +
+
+
Route Start Time: {currentRoute?.start_time && (new Date(currentRoute?.start_time))?.toLocaleTimeString()}
+
Route End Time: {currentRoute?.end_time && (new Date(currentRoute?.end_time))?.toLocaleTimeString()}
+ {currentRoute?.type === 'inbound' &&
Arrive Center Time: {currentRoute?.end_time && (new Date(currentRoute?.end_time))?.toLocaleTimeString()}
} + {currentRoute?.type === 'outbound' &&
Leave Center Time: {currentRoute?.start_time && (new Date(currentRoute?.start_time))?.toLocaleTimeString()}
} + {currentRoute?.type === 'outbound' &&
Estimated Start Time: {currentRoute?.estimated_start_time && (new Date(currentRoute?.estimated_start_time))?.toLocaleTimeString()}
} +
+
+
Start Mileage: { currentRoute?.start_mileage}
+
End Mileage: { currentRoute?.end_mileage}
+ +
+
{!signature && signatureRequest &&
+
Please send this to the driver to get signature:
+
{`${window.location.origin}/signature/${signatureRequest?.id}`}
+
}
+
+
+
+ {currentRoute && } +
+
+

+
+
Checklist:
+ {currentRoute && currentRoute?.checklist_result?.map(item =>
{`${item?.item}: ${item?.result ? 'Yes': "No"}`}
)} + {currentRoute && currentRoute?.checklist_result.length === 0 && <>No Checklist found} +
+
+ + closeModal()}> + + Vehicle Info + + + <> +
Vehicle Number: {currentVehicle?.vehicle_number}
+
Tag: {currentVehicle?.tag}
+
EzPass: {currentVehicle?.ezpass}
+
GPS: {currentVehicle?.gps_tag}
+
Capacity: {currentVehicle?.capacity}
+
Status: {currentVehicle?.status}
+
Mileage: {currentVehicle?.mileage}
+ +
+ + + +
+ + + ); +}; + +export default RouteView; \ No newline at end of file diff --git a/client/src/components/trans-routes/RoutesDashboard.js b/client/src/components/trans-routes/RoutesDashboard.js new file mode 100644 index 0000000..ec96757 --- /dev/null +++ b/client/src/components/trans-routes/RoutesDashboard.js @@ -0,0 +1,259 @@ +import React, { useState, useEffect } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import { selectInboundRoutes, selectOutboundRoutes, selectAllBreakfastRecords, selectAllLunchRecords, selectAllSnackRecords, selectAllRoutes, selectAllActiveVehicles, selectAllActiveDrivers, transRoutesSlice } from "./../../store"; +import RoutesSection from "./RoutesSection"; +import PersonnelSection from "./PersonnelSection"; +import { AuthService, CustomerService, TransRoutesService } from "../../services"; +import { PERSONAL_ROUTE_STATUS, ROUTE_STATUS, reportRootUrl } from "../../shared"; +import BreakfastSection from './BreakfastSection'; +import moment from 'moment'; +import LunchSection from "./LunchSection"; +import SnackSection from "./SnackSection"; + + +const RoutesDashboard = () => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const { fetchAllRoutes, createRoute, fetchAllBreakfastRecords, fetchAllLunchRecords, fetchAllSnackRecords } = transRoutesSlice.actions; + const inboundRoutes = useSelector(selectInboundRoutes); + const outboundRoutes = useSelector(selectOutboundRoutes); + const allRoutes = useSelector(selectAllRoutes); + const drivers = useSelector(selectAllActiveDrivers); + const vehicles = useSelector(selectAllActiveVehicles); + const breakfastRecords = useSelector(selectAllBreakfastRecords); + const lunchRecords = useSelector(selectAllLunchRecords); + const snackRecords = useSelector(selectAllSnackRecords); + const [showCopyDateLoading, setShowCopyDateLoading] = useState(false); + const [showSyncCustomersLoading, setShowSyncCustomersLoading] = useState(false); + const now = new Date(); + const yesterday = new Date(); + const tomorrow = new Date(); + yesterday.setDate(now.getDate() - 1); + tomorrow.setDate(now.getDate() + 1); + const getDateString = (date) => { + return ((date.getMonth() > 8) ? (date.getMonth() + 1) : ('0' + (date.getMonth() + 1))) + '/' + ((date.getDate() > 9) ? date.getDate() : ('0' + date.getDate())) + '/' + date.getFullYear() + } + const directToSchedule = () => { + navigate(`/trans-routes/schedule?dateSchedule=${moment(tomorrow).format('YYYY-MM-DD')}`); + } + const generateRouteReport = () => { + window.open(`${reportRootUrl}?token=${localStorage.getItem('token')}&date=${getDateString(now)}`, '_blank'); + } + const goToHistoryPage = () => { + navigate('/trans-routes/history'); + } + const goToSignature = () => { + navigate('/trans-routes/route-signature'); + } + const createNewRoute = () => { + navigate('/trans-routes/create') + } + const copyYesterdayRoutes = () => { + setShowCopyDateLoading(true); + TransRoutesService.getAll(getDateString(yesterday)).then(({data: yesterdayRoutes}) => { + for (const draftRoute of yesterdayRoutes) { + if (draftRoute) { + const data = Object.assign({}, { + name: draftRoute.name, + schedule_date: ((now.getMonth() > 8) ? (now.getMonth() + 1) : ('0' + (now.getMonth() + 1))) + '/' + ((now.getDate() > 9) ? now.getDate() : ('0' + now.getDate())) + '/' + now.getFullYear(), + vehicle: draftRoute.vehicle, + driver: draftRoute.driver, + type: draftRoute.type, + start_mileage: vehicles.find((vehicle) => vehicle.id === draftRoute.vehicle)?.mileage, + route_customer_list: draftRoute.route_customer_list?.map((customer) => { + return Object.assign({}, customer, { + customer_enter_center_time: null, + customer_leave_center_time: null, + customer_pickup_time: null, + customer_dropoff_time: null, + customer_estimated_pickup_time: null, + customer_estimated_dropoff_time: null, + customer_route_status: PERSONAL_ROUTE_STATUS.NO_STATUS + }) + }) + }); + dispatch(createRoute({ data })); + } + } + setTimeout(() => { + dispatch(fetchAllRoutes()); + setShowCopyDateLoading(false); + }, 2000); + }); + } + + const cleanAllRoutesStatus = () => { + for (const route of allRoutes) { + let cleanRoute = Object.assign({}, route, { status: [ROUTE_STATUS.NOT_STARTED] }); + let newCustomers = []; + for (let i=0; i< cleanRoute.route_customer_list.length; i++) { + const newCustomerListObject = { + ...cleanRoute.route_customer_list[i], + customer_enter_center_time: null, + customer_leave_center_time: null, + customer_pickup_time: null, + customer_dropoff_time: null, + customer_estimated_pickup_time: null, + customer_estimated_dropoff_time: null, + customer_route_status: cleanRoute.route_customer_list[i].customer_route_status === PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT ? PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT : PERSONAL_ROUTE_STATUS.NO_STATUS + } + newCustomers.push(newCustomerListObject); + } + cleanRoute = Object.assign({}, cleanRoute, {route_customer_list: newCustomers}); + TransRoutesService.updateRoute(route.id, cleanRoute); + } + setTimeout(dispatch(fetchAllRoutes()), 10000); + } + const syncCustomersInfo = () => { + setShowSyncCustomersLoading(true); + CustomerService.getAllCustomers().then(data => { + const customers = data.data; + const customersMap = new Map(); + for (const customer of customers) { + customersMap.set(customer.id, { + customer_name: `${customer.name} ${customer.name_cn?.length > 0 ? `(${customer.name_cn})` : ``}`, + customer_type: customer.type, + customer_pickup_status: customer.pickup_status, + customer_note: customer.note, + customer_special_needs: customer.special_needs, + customer_phone: customer.phone || customer.mobile_phone || customer.home_phone, + customer_table_id: customer.table_id, + customer_language: customer.language + }) + }; + for (const route of allRoutes) { + const customerList = route.route_customer_list; + const newCustomerList = customerList.map((customerItem) => Object.assign({}, customerItem, customersMap.get(customerItem.customer_id))); + const newRouteObject = Object.assign({}, route, {route_customer_list: newCustomerList}); + TransRoutesService.updateRoute(route.id, newRouteObject); + } + setTimeout(() => { dispatch(fetchAllRoutes()); setShowSyncCustomersLoading(false);}, 5000); + }) + } + const confimHasBreakfast = (customer) => { + TransRoutesService.createBreakfastRecords({ + customer_id: customer?.customer_id, + customer_name: customer?.customer_name, + has_breakfast: true, + date: moment(new Date()).format('MM/DD/YYYY'), + create_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, + create_date: new Date(), + edit_history: [{ + employee: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, + date: new Date() + }] + }).then(() => { + dispatch(fetchAllBreakfastRecords()); + }) + } + + const confirmHasLunch = (customer) => { + TransRoutesService.createLunchRecords({ + customer_id: customer?.customer_id, + customer_name: customer?.customer_name, + has_lunch: true, + date: moment(new Date()).format('MM/DD/YYYY'), + create_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, + create_date: new Date(), + edit_history: [{ + employee: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, + date: new Date() + }] + }).then(() => { + dispatch(fetchAllLunchRecords()); + }) + } + + const confirmHasSnack = (customer) => { + TransRoutesService.createSnackRecords({ + customer_id: customer?.customer_id, + customer_name: customer?.customer_name, + has_snack: true, + date: moment(new Date()).format('MM/DD/YYYY'), + create_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, + create_date: new Date(), + edit_history: [{ + employee: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, + date: new Date() + }] + }).then(() => { + dispatch(fetchAllSnackRecords()); + }) + } + + const removeBreakfastRecord = (customer_id) => { + const breakfast = breakfastRecords.find(b => b.customer_id === customer_id)?.id; + TransRoutesService.deleteBreakfastRecords(breakfast).then(() => { + dispatch(fetchAllBreakfastRecords()); + }) + } + const removeLunchRecord = (customer_id) => { + const lunch = lunchRecords.find(b => b.customer_id === customer_id)?.id; + TransRoutesService.deleteLunchRecords(lunch).then(() => { + dispatch(fetchAllLunchRecords()); + }) + } + const removeSnackRecord = (customer_id) => { + const snack = snackRecords.find(b => b.customer_id === customer_id)?.id; + TransRoutesService.deleteSnackRecords(snack).then(() => { + dispatch(fetchAllSnackRecords()); + }) + } + return ( + <> +
+
+
Today's Route Status
+
+
+
+
+ Today's Date: { new Date().toLocaleDateString() } +
+
+ + + {AuthService.canAddOrEditRoutes() && + + } + + + + + +
+
+
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
+ + + ); +}; + +export default RoutesDashboard; \ No newline at end of file diff --git a/client/src/components/trans-routes/RoutesHistory.js b/client/src/components/trans-routes/RoutesHistory.js new file mode 100644 index 0000000..20ac4d4 --- /dev/null +++ b/client/src/components/trans-routes/RoutesHistory.js @@ -0,0 +1,77 @@ +import React, {useState} from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import { transRoutesSlice ,selectHistoryInboundRoutes, selectHistoryOutboundRoutes, selectHistoryRoutes, selectAllActiveDrivers, selectAllActiveVehicles } from "./../../store"; +import RoutesSection from "./RoutesSection"; +import PersonnelSection from "./PersonnelSection"; +import DatePicker from "react-datepicker"; +import { AuthService } from "../../services"; +import {reportRootUrl} from "../../shared"; + +const RoutesHistory = () => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const inboundRoutes = useSelector(selectHistoryInboundRoutes); + const outboundRoutes = useSelector(selectHistoryOutboundRoutes); + const allRoutes = useSelector(selectHistoryRoutes); + const [selectedDate, setSelectedDate] = useState(undefined); + const drivers = useSelector(selectAllActiveDrivers); + const vehicles = useSelector(selectAllActiveVehicles); + const backToDashboard = () => { + navigate('/trans-routes/dashboard'); + } + const { fetchAllHistoryRoutes } = transRoutesSlice.actions; + const getDateString = (date) => { + return ((date.getMonth() > 8) ? (date.getMonth() + 1) : ('0' + (date.getMonth() + 1))) + '/' + ((date.getDate() > 9) ? date.getDate() : ('0' + date.getDate())) + '/' + date.getFullYear() + } + const searchForHistory = () => { + if (selectedDate) { + dispatch(fetchAllHistoryRoutes({dateText: getDateString(selectedDate)})); + } + }; + const generateRouteReport = () => { + window.open(`${reportRootUrl}?token=${localStorage.getItem('token')}&date=${getDateString(selectedDate)}`, '_blank'); + } + + return ( + <> +
+
+
Route History
+
+
+
+
+ Select A Date: +
+
+ setSelectedDate(v)} /> +
+
+ +
+ {selectedDate &&
+ Selected Date: {getDateString(new Date(selectedDate))} +
} +
+
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+ + + ); +}; + +export default RoutesHistory; \ No newline at end of file diff --git a/client/src/components/trans-routes/RoutesSchedule.js b/client/src/components/trans-routes/RoutesSchedule.js new file mode 100644 index 0000000..535173b --- /dev/null +++ b/client/src/components/trans-routes/RoutesSchedule.js @@ -0,0 +1,339 @@ +import React, { useEffect, useState } from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { useNavigate, useSearchParams } from "react-router-dom"; +import { selectAllRoutes, selectTomorrowAllRoutes, selectHistoryRoutes, selectTomorrowInboundRoutes, selectTomorrowOutboundRoutes, selectAllActiveDrivers, selectAllActiveVehicles } from "./../../store"; +import RoutesSection from "./RoutesSection"; +import { PERSONAL_ROUTE_STATUS } from "../../shared"; +import { transRoutesSlice } from "../../store"; +import { AuthService, TransRoutesService } from "../../services"; +import DatePicker from "react-datepicker"; +import "react-datepicker/dist/react-datepicker.css"; +import { Modal, Button, Spinner } from "react-bootstrap"; +import RouteCustomerTable from "./RouteCustomerTable"; +import moment from "moment"; + +const RoutesSchedule = () => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const inboundRoutes = useSelector(selectTomorrowInboundRoutes); + const outboundRoutes = useSelector(selectTomorrowOutboundRoutes); + const allRoutes = useSelector(selectAllRoutes); + const tomorrowRoutes = useSelector(selectTomorrowAllRoutes); + const drivers = useSelector(selectAllActiveDrivers); + const [datePicked, setDatePicked] = useState(undefined); + const [dateTargetPicked, setDateTargetPicked] = useState(undefined); + const [dateSchedule, setDateSchedule] = useState(undefined); + const vehicles = useSelector(selectAllActiveVehicles); + const { createRoute, fetchAllTomorrowRoutes } = transRoutesSlice.actions; + const [errorMessage, setErrorMessage] = useState(undefined); + const [successMessage, setSuccessMessage] = useState(undefined); + const [showCopyTodayLoading, setShowCopyTodayLoading] = useState(false); + const [showCopyDateLoading, setShowCopyDateLoading] = useState(false); + const [showCopyDateTargetLoading, setShowCopyDateTargetLoading] = useState(false); + const [copyDisabled, setCopyDisabled] = useState(false); + const [copyDateDisabled, setCopyDateDisabled] = useState(false); + const [showDeleteModal, setShowDeleteModal] = useState(false); + const [searchParams, setSearchParams] = useSearchParams(); + const [isLoading, setIsLoading] = useState(false); + const now = new Date(); + const params = new URLSearchParams(window.location.search); + const scheduleDate = params.get('dateSchedule'); + + now.setDate(now.getDate() + 1); + const directToDashboad = () => { + navigate('/trans-routes/dashboard'); + } + + const createVehicle = () => { + navigate('/vehicles?redirect=schedule'); + } + + const createDriver = () => { + navigate('/employees?redirect=schedule&type=driver'); + } + + const goToCreateRoute = (type) => { + navigate(`/trans-routes/create?type=${type}&date=${scheduleDate? scheduleDate: 'tomorrow'}`); + } + + const getDateString = (date) => { + return ((date.getMonth() > 8) ? (date.getMonth() + 1) : ('0' + (date.getMonth() + 1))) + '/' + ((date.getDate() > 9) ? date.getDate() : ('0' + date.getDate())) + '/' + date.getFullYear() + } + + const cleanupSchedule = () => { + tomorrowRoutes.forEach((route) => { + TransRoutesService.deleteRoute(route.id); + }); + setTimeout(() => { + closeDeleteModal(); + window.location.reload(); + }, 1000) + } + + const closeDeleteModal = () => { + setShowDeleteModal(false); + } + + + const triggerShowDeleteModal = () => { + setShowDeleteModal(true); + } + + const startToScheduleDate = (v) => { + setDateSchedule(v); + setSearchParams({dateSchedule: moment(v).format('YYYY-MM-DD')}); + dispatch(fetchAllTomorrowRoutes({dateText: moment(v).format('MM/DD/YYYY')})); + } + + const validateSchedule = () => { + const inboundCustomersRouteMap = {}; + let success = true; + for (const inboundRoute of inboundRoutes) { + for (const inboundCustomer of inboundRoute.route_customer_list) { + if (Object.keys(inboundCustomersRouteMap).includes(inboundCustomer.customer_id) && inboundCustomer.customer_route_status !== PERSONAL_ROUTE_STATUS.DISABLED) { + setSuccessMessage(undefined); + success = false; + setErrorMessage(`Error: Customer ${inboundCustomer.customer_name} was scheduled in both inbound ${inboundRoute.name} and ${inboundCustomersRouteMap[inboundCustomer.customer_id].name}.`) + break; + } else { + if (inboundCustomer.customer_route_status !== PERSONAL_ROUTE_STATUS.DISABLED) { + inboundCustomersRouteMap[inboundCustomer.customer_id] = inboundRoute; + } + + } + } + } + const outboundCustomersRouteMap = {}; + for (const outboundRoute of outboundRoutes) { + for (const outboundCustomer of outboundRoute.route_customer_list) { + if (Object.keys(outboundCustomersRouteMap).includes(outboundCustomer.customer_id) && outboundCustomer.customer_route_status !== PERSONAL_ROUTE_STATUS.DISABLED) { + setSuccessMessage(undefined); + success = false; + setErrorMessage(`Error: Customer ${outboundCustomer.customer_name} was scheduled in both outbound ${outboundRoute.name} and ${outboundCustomersRouteMap[outboundCustomer.customer_id].name}.`) + break; + } else { + if (outboundCustomer.customer_route_status !== PERSONAL_ROUTE_STATUS.DISABLED) { + outboundCustomersRouteMap[outboundCustomer.customer_id] = outboundRoute; + } + + } + } + } + if (success) { + setErrorMessage(undefined); + setSuccessMessage(`Routes Schedule Validate Successfully`); + } + } + + const copyTodayRoutesOver = () => { + setShowCopyTodayLoading(true); + setIsLoading(true); + let count = 0; + TransRoutesService.getAll(((now.getMonth() > 8) ? (now.getMonth() + 1) : ('0' + (now.getMonth() + 1))) + '/' + ((now.getDate() > 9) ? now.getDate() : ('0' + now.getDate())) + '/' + now.getFullYear()).then(existingRoutes => { + for (const draftRoute of allRoutes) { + const existed = existingRoutes?.data?.find(r => r.name === draftRoute.name); + if (draftRoute && !existed) { + const data = Object.assign({}, { + name: draftRoute.name, + schedule_date: ((now.getMonth() > 8) ? (now.getMonth() + 1) : ('0' + (now.getMonth() + 1))) + '/' + ((now.getDate() > 9) ? now.getDate() : ('0' + now.getDate())) + '/' + now.getFullYear(), + vehicle: draftRoute.vehicle, + driver: draftRoute.driver, + type: draftRoute.type, + start_mileage: vehicles.find((vehicle) => vehicle.id === draftRoute.vehicle)?.mileage, + route_customer_list: draftRoute.route_customer_list?.map((customer) => { + return Object.assign({}, customer, { + customer_enter_center_time: null, + customer_leave_center_time: null, + customer_pickup_time: null, + customer_dropoff_time: null, + customer_estimated_pickup_time: null, + customer_estimated_dropoff_time: null, + customer_route_status: PERSONAL_ROUTE_STATUS.NO_STATUS, + customer_address_override: null + }) + }) + }); + dispatch(createRoute({ data })); + } else { + if (existed) { + count++; + } + } + } + setTimeout(() => { + dispatch(fetchAllTomorrowRoutes({})); + setShowCopyTodayLoading(false); + setCopyDisabled(true); + setIsLoading(false); + setSuccessMessage('Routes Copied Successfully, please do not click the button again!'); + if (count > 0) { + window.alert(`${count} routes has existed in selected date and is not copied again!`) + } + }, 2000); + }) + + } + + const copyDateRoutesOver = (withTargetDate) => { + const dateSelected = new Date(datePicked); + const scheduleDate = withTargetDate ? new Date(dateTargetPicked) : now; + if (withTargetDate) { + setShowCopyDateTargetLoading(true); + } else { + setShowCopyDateLoading(true); + } + + setIsLoading(true); + let count = 0; + TransRoutesService.getAll(getDateString(dateSelected)).then(({data: allHistoryRoutes}) => { + TransRoutesService.getAll(((scheduleDate.getMonth() > 8) ? (scheduleDate.getMonth() + 1) : ('0' + (scheduleDate.getMonth() + 1))) + '/' + ((scheduleDate.getDate() > 9) ? scheduleDate.getDate() : ('0' + scheduleDate.getDate())) + '/' + scheduleDate.getFullYear()).then(existingRoutes => { + for (const draftRoute of allHistoryRoutes) { + const existed = existingRoutes?.data?.find((r) => r. name === draftRoute.name); + if (draftRoute && !existed) { + const data = Object.assign({}, { + name: draftRoute.name, + schedule_date: ((scheduleDate.getMonth() > 8) ? (scheduleDate.getMonth() + 1) : ('0' + (scheduleDate.getMonth() + 1))) + '/' + ((scheduleDate.getDate() > 9) ? scheduleDate.getDate() : ('0' + scheduleDate.getDate())) + '/' + scheduleDate.getFullYear(), + vehicle: draftRoute.vehicle, + driver: draftRoute.driver, + type: draftRoute.type, + start_mileage: vehicles.find((vehicle) => vehicle.id === draftRoute.vehicle)?.mileage, + route_customer_list: draftRoute.route_customer_list?.map((customer) => { + return Object.assign({}, customer, { + customer_enter_center_time: null, + customer_leave_center_time: null, + customer_pickup_time: null, + customer_dropoff_time: null, + customer_estimated_pickup_time: null, + customer_estimated_dropoff_time: null, + customer_route_status: PERSONAL_ROUTE_STATUS.NO_STATUS, + customer_address_override: null + }) + }) + }); + dispatch(createRoute({ data })); + } else { + if (existed) { + count++; + } + } + } + setTimeout(() => { + // dispatch(fetchAllTomorrowRoutes({})); + startToScheduleDate(withTargetDate ? dateTargetPicked : now); + if (withTargetDate) { + setShowCopyDateTargetLoading(false); + } else { + setShowCopyDateLoading(false); + } + setIsLoading(false); + setCopyDateDisabled(true); + setSuccessMessage('Routes Copied Successfully, please do not click the button again!'); + if (count > 0) { + window.alert(`${count} routes has existed on selected date and are not copied again.`) + } + }, 2000); + }); + }); + } + + useEffect(() => { + if (!AuthService.canAddOrEditRoutes()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + }, []); + + return ( + <> + {errorMessage &&
+ {errorMessage} + +
} + {successMessage &&
+ {successMessage} + +
} +
+
+ Schedule Date: { scheduleDate ? scheduleDate : moment().format('YYYY-MM-DD')} +
+
+
+
+ +
+
+ +
+
+ +
+
+ setDatePicked(v)} /> +
+
+ +
+
+ -- OR -- +
+
+ setDateTargetPicked(v)} /> +
+
+ +
+ +
+ {startToScheduleDate(v)}} /> +
+ + {isLoading &&
+ Loading... +
} + {!isLoading && <> +
+ +
+
+
+ +
+
+ {(AuthService.canCreateOrEditDrivers() || AuthService.canAddOrEditEmployees()) &&
+ +
} +
+ {AuthService.canAddOrEditVechiles() &&
+ +
} +
+
+ {allRoutes && vehicles && } +
+ } + +
+ closeDeleteModal()}> + + Delete Schedule + + +
Are you sure you want to delete all the schedule?
+
+ + + + +
+ + + ); +}; + +export default RoutesSchedule; \ No newline at end of file diff --git a/client/src/components/trans-routes/RoutesSection.js b/client/src/components/trans-routes/RoutesSection.js new file mode 100644 index 0000000..759ba99 --- /dev/null +++ b/client/src/components/trans-routes/RoutesSection.js @@ -0,0 +1,147 @@ +import React, {useState} from "react"; +import { useDispatch } from "react-redux"; +import { transRoutesSlice } from "../../store"; +import RouteCard from "./RouteCard"; +import { Modal, Button } from "react-bootstrap"; +import { PERSONAL_ROUTE_STATUS, PICKUP_STATUS } from "../../shared"; +import { AuthService } from "../../services"; +import moment from 'moment'; + +const RoutesSection = ({transRoutes, copyList, sectionName, drivers, vehicles, canAddNew, addText, copyText, redirect, routeType}) => { + const [showCopyModal, setShowCopyModal] = useState(false); + const [routesOptions, setRouteOptions] = useState([]); + const [newRoute, setNewRoute] = useState(undefined); + const [newName, setNewName] = useState(''); + const dispatch = useDispatch(); + const { createRoute } = transRoutesSlice.actions; + const params = new URLSearchParams(window.location.search); + const scheduleDate = params.get('dateSchedule'); + + const openCopyModal = () => { + setRouteOptions(copyList); + setShowCopyModal(true); + } + + const closeCopyModal = () => { + setRouteOptions([]); + setNewRoute(undefined); + setNewName(''); + setShowCopyModal(false); + } + + const saveRoute = () => { + if (newRoute && newName) { + const payload = Object.assign({}, copyList.find((item) => item.id === newRoute), { type: routeType, name: newName}); + delete payload.id; + const customers = payload.route_customer_list.slice().sort((a, b) => b.customer_pickup_order - a.customer_pickup_order); + let n = 0; + for (let i=0; i< customers.length -1 ; i++) { + if (i === 0) { + customers[i] = Object.assign({}, customers[i], {customer_pickup_order_new: n}); + } + if (customers[i+1].customer_pickup_order === customers[i].customer_pickup_order) { + customers[i+1] = Object.assign({}, customers[i+1], {customer_pickup_order_new: n}); + } else { + n++; + customers[i+1] = Object.assign({}, customers[i+1], {customer_pickup_order_new: n}); + } + } + const customersPayload = customers.map(customer => { + let newCustomer = { + ...customer, + customer_pickup_order: customer.customer_pickup_order_new + } + delete newCustomer.customer_pickup_order_new; + return newCustomer; + }); + const finalPayload = Object.assign({}, payload, {route_customer_list: customersPayload}); + const fetchParam = scheduleDate ? { fetchDate: moment(scheduleDate).format('MM/DD/YYYY') } : {fetchTomorrow: true}; + dispatch(createRoute(Object.assign({}, {data: finalPayload}, fetchParam))); + closeCopyModal(); + } + } + + const getSeniorsNumber = () => { + const seniors = []; + for (const transRoute of transRoutes) { + for (const customer of transRoute.route_customer_list) { + if (!seniors.includes(customer.customer_id) && customer.customer_pickup_status !== PICKUP_STATUS.SCHEDULE_ABSENT && customer.customer_route_status !== PERSONAL_ROUTE_STATUS.DISABLED) { + seniors.push(customer.customer_id); + } + } + } + return seniors.length; + } + + + return ( + <> +
+ + {sectionName} { (sectionName.includes('Inbound') || sectionName.includes('Outbound')) && (` (${getSeniorsNumber()} participants)`)} + + { canAddNew && ( + { if (routeType) {redirect(routeType)} else {redirect()}}}> + {addText} + )} + { + copyText && ( + {openCopyModal()} }> + {copyText} + )} +
+ { + transRoutes && (<> +
+ { + transRoutes.map((r) => Object.assign({}, r, {vehicleNumber: vehicles?.find((v) => r.vehicle === v.id)?.vehicle_number || 0})).sort((a, b) => a?.vehicleNumber - b?.vehicleNumber).map((route) =>
) + } +
+ ) + } + { + AuthService.canViewDrivers() && !transRoutes && drivers && (<> +
+ { + drivers.map((driver) =>
) + } +
+ ) + } + { + AuthService.canViewVechiles() && !transRoutes && vehicles && (<> +
+ { + vehicles.map((vehicle) =>
) + } +
+ ) + } + closeCopyModal()}> + + Copy Route + + + <> +
Select an Existed Route:
+
Type in New Route Name: {setNewName(e.currentTarget.value)}} />
+ +
+ + + + +
+ + + ); +}; + +export default RoutesSection; \ No newline at end of file diff --git a/client/src/components/trans-routes/RoutesSignature.js b/client/src/components/trans-routes/RoutesSignature.js new file mode 100644 index 0000000..e0d08f5 --- /dev/null +++ b/client/src/components/trans-routes/RoutesSignature.js @@ -0,0 +1,237 @@ +import React, {useState, useEffect} from "react"; +import { useSelector } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import { CustomerService, DriverService, EmployeeService, EventsService, TransRoutesService } from "../../services"; +import { selectAllActiveDrivers, selectAllActiveVehicles } from "./../../store"; +import moment from 'moment'; +import DatePicker from "react-datepicker"; +import { PERSONAL_ROUTE_STATUS, ROUTE_STATUS } from "../../shared"; + +const RouteSignatureList = () => { + const params = new URLSearchParams(window.location.search); + const drivers = useSelector(selectAllActiveDrivers); + const vehicles = useSelector(selectAllActiveVehicles); + + const navigate = useNavigate(); + const [dateSelected, setDateSelected] = useState(new Date()); + const [routes, setRoutes] = useState([]); + const [directorSignature, setDirectorSignature] = useState(undefined); + const [selectedFile, setSelectedFile] = useState(); + const [selectedDriver, setSelectedDriver] = useState(undefined); + const [driverList, setDriverList] = useState([]); + const [customers, setCustomers] = useState([]); + + const redirectToDashboard = () => { + navigate(`/trans-routes/dashboard`); + } + + const uploadDirectorSignature = () => { + const formData = new FormData(); + const site = EventsService.site; + formData.append("file", selectedFile); + if (selectedFile) { + if (directorSignature) { + CustomerService.deleteFile({'name': `center_director_signature_site_${site}`}).then(() => { + CustomerService.uploadAvatar(`center_director_signature_site_${site}`, formData).then(() => { + CustomerService.getAvatar(`center_director_signature_site_${site}`).then(data => { + if (data?.data) { + setDirectorSignature(data?.data) + } + }); + }) + }) + } else { + CustomerService.uploadAvatar(`center_director_signature_site_${site}`, formData).then(() => { + CustomerService.getAvatar(`center_director_signature_site_${site}`).then(data => { + if (data?.data) { + setDirectorSignature(data?.data) + } + }); + }) + } + } + + } + + const getAllUniqueCustomers = (routes) => { + let result = []; + for (const route of routes) { + const customerList = route.route_customer_list.map(item => Object.assign({}, item, {routeType: route.type, routeId: route.id, route: route, customer_status_inbound: route.type === 'inbound' && item.customer_route_status, customer_status_outbound: route.type === 'outbound' && item.customer_route_status, inbound: route.type === 'inbound' && route, outbound: route.type === 'outbound' && route})) + for (const customer of customerList) { + const existItem = result.find((item => (item.customer_id === customer.customer_id) || (item?.customer_name?.replaceAll(' ', '')?.toLowerCase() === customer?.customer_name?.replaceAll(' ', '')?.toLowerCase()) )); + if (existItem) { + result = result.filter(item => item !== existItem); + const newItem = Object.assign({}, existItem, { + customer_enter_center_time: existItem?.customer_enter_center_time || customer?.customer_enter_center_time, + customer_leave_center_time: existItem?.customer_leave_center_time || customer?.customer_leave_center_time, + customer_pickup_time: existItem?.customer_pickup_time || customer?.customer_pickup_time, + customer_dropoff_time: existItem?.customer_dropoff_time || customer?.customer_dropoff_time, + inbound: existItem?.inbound || customer?.inbound, + outbound: existItem?.outbound || customer?.outbound, + customer_status_inbound: existItem?.customer_status_inbound || customer?.customer_status_inbound, + customer_status_outbound: existItem?.customer_status_outbound || customer?.customer_status_outbound + }) + result.push(newItem); + } else { + result.push(customer); + } + } + } + return result.sort((a, b) => { + if (a.customer_name < b.customer_name) { + return -1; + } else { + return 1; + } + }); + } + + useEffect(() => { + DriverService.getAllActiveDrivers('driver', 'active').then((data) => { + setDriverList(data.data); + }); + CustomerService.getAllCustomers().then((data) => setCustomers(data?.data)); + }, []); + + useEffect(() => { + const site = EventsService.site; + CustomerService.getAvatar(`center_director_signature_site_${site}`).then(data => { + if (data?.data) { + setDirectorSignature(data?.data) + } + }); + TransRoutesService.getAll(moment(dateSelected)?.format('MM/DD/YYYY')).then(data => { + const routesResults = data.data; + const finalRoutes = routesResults.map(async (routeItem) => { + const dateArr = moment(dateSelected)?.format('MM/DD/YYYY')?.split('/') || []; + try { + const result = await CustomerService.getAvatar(`${routeItem.id}_${routeItem.driver}_${dateArr[0]}_${dateArr[1]}`); + return result?.data ? Object.assign({}, routeItem, {signature: result?.data}) : routeItem; + } catch (ex) { + return routeItem; + } + }); + Promise.all(finalRoutes).then(finalRoutesData => { + setRoutes(finalRoutesData); + }) + }) + }, [dateSelected]) + + + return ( + <> +
+
+
View Routes Signatures
+
+
+ +
+
+
+ Select Date to start: setDateSelected(v)} /> +
+
+ Filter By Driver: +
+ + + + + + + + + + + + { + routes && routes.filter((route) => { + if (!selectedDriver) { + return route; + } else { + return route?.driver === selectedDriver; + } + })?.map(({id, name, end_time, driver, type, signature}, index) => { + return ( + + + + + + ) + }) + } + +
Route NameDriverRoute End TimeRoute TypeSignature
{name}{drivers.find((d) => d.id === driver)?.name}{end_time? moment(end_time).format('HH:mm'): ''}{type} + {/* {images.find((img) => img.id === id)?.image && img.id === id)?.image}`}/>} */} + {signature && } +
+
+ + {moment(dateSelected)?.format('MM/DD/YYYY')} +
+
+ + + + + + + + + + + + + + + + + { + getAllUniqueCustomers(routes?.filter((route) => { + if (!selectedDriver) { + return route; + } else { + return route?.driver === selectedDriver; + } + }))?.map(({customer_name, customer_status_inbound, customer_status_outbound, customer_id, customer_enter_center_time, customer_dropoff_time, customer_leave_center_time, customer_pickup_time, inbound, outbound}, index) => { + console.log('customers', customers); + return ( + + + + + + + + + + + ) + }) + } + +
IndexCustomer NamePickup TimeEnter Center TimeLeave Center TimeDrop off TImeMA NumberInbound NameOutbound NameTotal Hours
{index+1}{customer_name}{customer_status_inbound !== PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT && customer_status_inbound !== PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT? (customer_pickup_time ? new Date(customer_pickup_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : '') : ''}{customer_status_inbound !== PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT && customer_status_inbound !== PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT ? (customer_enter_center_time ? new Date(customer_enter_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}): ''): ''}{customer_status_outbound !== PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT && customer_status_outbound !== PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT ? (customer_leave_center_time ? new Date(customer_leave_center_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}): ''): ''}{customer_status_outbound !== PERSONAL_ROUTE_STATUS.SCHEDULED_ABSENT && customer_status_outbound !== PERSONAL_ROUTE_STATUS.UNEXPECTED_ABSENT? (customer_dropoff_time ? new Date(customer_dropoff_time).toLocaleTimeString('en-US', {hour12: false, hour: '2-digit', minute: '2-digit'}) : ''): ''}{customers.length > 0 && customers.find(c => c?.id === customer_id || c?.name === customer_name)?.medicaid_number || customers.find(c => c?.id === customer_id)?.medicare_number}{inbound?.name || ''}{outbound?.name || ''}{customer_leave_center_time && customer_enter_center_time && Math.round((new Date(customer_leave_center_time) - new Date(customer_enter_center_time))/1000/3600) || ''}
+
+
+
Center Director Signature:
+ {directorSignature &&
} + {!directorSignature &&
No Director Signature Uploaded yet
} +
Upload Center Director New Signature:
+
setSelectedFile(e.target.files[0])} className="form-control-file border">
+
+
+
+ +
+
+ + + ); +}; + +export default RouteSignatureList; \ No newline at end of file diff --git a/client/src/components/trans-routes/SnackSection.js b/client/src/components/trans-routes/SnackSection.js new file mode 100644 index 0000000..6c99b23 --- /dev/null +++ b/client/src/components/trans-routes/SnackSection.js @@ -0,0 +1,76 @@ +import React, { useEffect, useState } from "react"; +import { TransRoutesService } from "../../services"; +import { useDispatch } from "react-redux"; +import { transRoutesSlice } from "./../../store"; +import moment from 'moment'; + + +const SnackSection = ({transRoutes, snackRecords, sectionName, confirmHasSnack, removeSnackRecord}) => { + const [customers, setCustomers] = useState([]); + const dispatch = useDispatch(); + const { fetchAllSnackRecords } = transRoutesSlice.actions; + + const createSnackForAll = async () => { + for (const c of customers) { + await TransRoutesService.createSnackRecords({ + customer_id: c?.customer_id, + customer_name: c?.customer_name, + has_snack: true, + date: moment(new Date()).format('MM/DD/YYYY'), + create_by: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, + create_date: new Date(), + edit_history: [{ + employee: localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.name, + date: new Date() + }] + }) + }; + dispatch(fetchAllSnackRecords()); + } + + useEffect(() => { + const routeCustomers = TransRoutesService.getAllInCenterCustomersFromRoutesForSnack(transRoutes, snackRecords); + setCustomers(routeCustomers); + }, [snackRecords, transRoutes]); + + + + + + return ( + <> +
{sectionName} {` (${customers?.length})`}
+ {snackRecords?.length <= 0 && (
)} +
+
+ + + + + + + + + + { + snackRecords?.length >0 && customers?.map((customer) => ( + + + + + + )) + } + +
NameHas Snack TodayChange Snack Status
{customer?.customer_name}{customer?.has_snack ? 'Yes': 'No'} + {!customer?.has_snack && } + {customer?.has_snack && } +
+
+
+ + + ); +}; + +export default SnackSection; \ No newline at end of file diff --git a/client/src/components/trans-routes/TransRoutes.js b/client/src/components/trans-routes/TransRoutes.js new file mode 100644 index 0000000..8cc427d --- /dev/null +++ b/client/src/components/trans-routes/TransRoutes.js @@ -0,0 +1,52 @@ +import React, { useEffect } from "react"; +import { useDispatch } from "react-redux"; +import { Outlet, useNavigate } from 'react-router-dom'; +import { transRoutesSlice, driverSlice, vehicleSlice, transRouteTemplatesSlice } from "./../../store"; +import { AuthService } from "../../services"; +import moment from "moment"; + +const TransRoutes = () => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const { fetchAllRoutes, fetchAllTomorrowRoutes, fetchAllBreakfastRecords, fetchAllLunchRecords, fetchAllSnackRecords } = transRoutesSlice.actions; + const { fetchAllDrivers } = driverSlice.actions; + const { fetchAllVehicles } = vehicleSlice.actions; + const { fetchAllRouteTemplates } = transRouteTemplatesSlice.actions; + const backToAdmin = () => { + navigate('/admin'); + } + useEffect(() => { + if (!AuthService.canAddOrEditRoutes() && !AuthService.canViewRoutes()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change a dispatcher or admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + dispatch(fetchAllDrivers()); + dispatch(fetchAllVehicles()); + dispatch(fetchAllRoutes()); + const params = new URLSearchParams(window.location.search); + const scheduleDate = params.get('dateSchedule'); + const requestBody = scheduleDate ? {dateText: moment(scheduleDate).format('MM/DD/YYYY')}: {}; + dispatch(fetchAllTomorrowRoutes(requestBody)); + dispatch(fetchAllRouteTemplates()); + dispatch(fetchAllBreakfastRecords()); + dispatch(fetchAllLunchRecords()); + dispatch(fetchAllSnackRecords()); + const timeInterval = setInterval( + () => { dispatch(fetchAllRoutes()); }, 180000 + ); + return () => clearInterval(timeInterval); + }, []) + + return ( +
+
+
+
+ +
+
+ ); +}; + +export default TransRoutes; \ No newline at end of file diff --git a/client/src/components/vehicles/CreateVehicle.js b/client/src/components/vehicles/CreateVehicle.js new file mode 100644 index 0000000..b41792e --- /dev/null +++ b/client/src/components/vehicles/CreateVehicle.js @@ -0,0 +1,119 @@ +import React, {useEffect, useState} from "react"; +import { useSelector,useDispatch } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import { vehicleSlice, selectVehicleError } from "./../../store"; +import { AuthService } from "../../services"; + +const CreateVehicle = () => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + useEffect(() => { + if (!AuthService.canAddOrEditVechiles()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an Dispatcher or admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + }, []); + const { createVehicle } = vehicleSlice.actions; + const [vehicleNumber, setVehicleNumber] = useState(); + const [tag, setTag] = useState(''); + const [make, setMake] = useState(''); + const [vehicleModel, setVehicleModel] = useState(''); + const [year, setYear] = useState(''); + const [ezpass, setEzpass] = useState(''); + const [gps, setGps] = useState(''); + const [mileage, setMileage] = useState(); + const [capacity, setCapacity] = useState(); + const [checklist, setChecklist] = useState(['']); + const error = useSelector(selectVehicleError); + + const redirectTo = () => { + const params = new URLSearchParams(window.location.search); + const redirect = params.get('redirect'); + if (redirect === 'schedule') { + navigate(`/trans-routes/schedule`); + } else { + navigate(`/trans-routes/dashboard`); + } + } + + const addItemToArray = () => { + const arr = [...checklist, '']; + setChecklist(arr); + } + + const saveVechile = () => { + const data = { + vehicle_number: vehicleNumber, + tag, + ezpass, + gps_tag: gps, + mileage, + capacity, + year, + make, + vehicle_model: vehicleModel, + status: 'active', + checklist + }; + dispatch(createVehicle({data, redirectFun: redirectTo})); + } + + + return ( + <> +
+
+
Create New Vehicle
+
+
+
+
+
Vehicle Number(*):
setVehicleNumber(e.target.value)}/> +
+
+
Tag(*):
setTag(e.target.value)}/> +
+
+
EZ Pass:
setEzpass(e.target.value)}/> +
+
+
GPS:
setGps(e.target.value)}/> +
+
+
Make:
setMake(e.target.value)}/> +
+
+
Model:
setVehicleModel(e.target.value)}/> +
+
+
Year:
setYear(e.target.value)}/> +
+
+
Mileage(*):
setMileage(e.target.value)}/> +
+
+
Capacity(*):
setCapacity(e.target.value)}/> +
+
+
Checklist(*):
+ {checklist.map((item, index) => (
setChecklist([...checklist].map((a, index1) => {if (index1 === index) {return e.target.value;} return a;}))}/> + +
))} + +
+
+
+ {error &&
+ {error} +
} +
+ + +
+
+ + ); +}; + +export default CreateVehicle; \ No newline at end of file diff --git a/client/src/components/vehicles/UpdateVehicle.js b/client/src/components/vehicles/UpdateVehicle.js new file mode 100644 index 0000000..f28c6a5 --- /dev/null +++ b/client/src/components/vehicles/UpdateVehicle.js @@ -0,0 +1,165 @@ +import React, {useEffect, useState} from "react"; +import { useSelector,useDispatch } from "react-redux"; +import { useNavigate, useParams } from "react-router-dom"; +import { vehicleSlice, selectVehicleError } from "./../../store"; +import { AuthService } from "../../services"; + +const UpdateVehicle = () => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const params = useParams(); + const vehicles = useSelector((state) => state.vehicles && state.vehicles.vehicles); + const currentVehicle = vehicles.find(item => item.id === params.id ) || undefined; + const { updateVehicle, deleteVehicle, fetchAllVehicles } = vehicleSlice.actions; + const [vehicleNumber, setVehicleNumber] = useState(); + const [tag, setTag] = useState(''); + const [make, setMake] = useState(''); + const [vehicleModel, setVehicleModel] = useState(''); + const [year, setYear] = useState(''); + const [ezpass, setEzpass] = useState(''); + const [gps, setGps] = useState(''); + const [mileage, setMileage] = useState(); + const [capacity, setCapacity] = useState(); + const [checklist, setChecklist] = useState(['']); + const error = useSelector(selectVehicleError); + + useEffect(() => { + if (!AuthService.canAddOrEditVechiles()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an Dispatcher or admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + if (!currentVehicle) { + dispatch(fetchAllVehicles()); + } + }, []); + + useEffect(() => { + if (currentVehicle) { + setVehicleNumber(currentVehicle.vehicle_number); + setTag(currentVehicle.tag); + setMake(currentVehicle.make); + setVehicleModel(currentVehicle.vehicle_model); + setYear(currentVehicle.year); + setGps(currentVehicle.gps_tag); + setEzpass(currentVehicle.ezpass); + setMileage(currentVehicle.mileage); + setCapacity(currentVehicle.capacity); + setChecklist(currentVehicle.checklist); + } + + }, [currentVehicle]) + + const redirectTo = () => { + const params = new URLSearchParams(window.location.search); + const redirect = params.get('redirect'); + if (redirect === 'schedule') { + navigate(`/trans-routes/schedule`); + } else { + if (redirect === 'list') { + navigate(`/vehicles/list`) + } else { + navigate(`/trans-routes/dashboard`); + } + } + } + + const addItemToArray = () => { + const arr = [...checklist, '']; + setChecklist(arr); + } + + const saveVechile = () => { + const data = { + vehicle_number: vehicleNumber, + tag, + ezpass, + gps_tag: gps, + mileage, + capacity, + year, + make, + vehicle_model: vehicleModel, + status: 'active', + checklist + }; + dispatch(updateVehicle({id: params.id, data, redirectFun: redirectTo})); + } + + const deactivateVehicle = () => { + const data = { + vehicle_number: vehicleNumber, + tag, + ezpass, + gps_tag: gps, + mileage, + capacity, + year, + make, + vehicle_model: vehicleModel, + status: 'inactive', + checklist + }; + dispatch(deleteVehicle({id: params.id, data})); + redirectTo(); + } + + + return ( + <> +
+
+
Update Vehicle
+
+
+
+
+
Vehicle Number(*):
setVehicleNumber(e.target.value)}/> +
+
+
Tag(*):
setTag(e.target.value)}/> +
+
+
EZ Pass:
setEzpass(e.target.value)}/> +
+
+
GPS:
setGps(e.target.value)}/> +
+
+
Make:
setMake(e.target.value)}/> +
+
+
Model:
setVehicleModel(e.target.value)}/> +
+
+
Year:
setYear(e.target.value)}/> +
+
+
Mileage(*):
setMileage(e.target.value)}/> +
+
+
Capacity(*):
setCapacity(e.target.value)}/> +
+
+
Checklist(*):
+ {checklist.map((item, index) => (
setChecklist([...checklist].map((a, index1) => {if (index1 === index) {return e.target.value;} return a;}))}/> + +
))} + +
+
+
+ {error &&
+ {error} +
} +
+ + + +
+
+ + ); +}; + +export default UpdateVehicle; \ No newline at end of file diff --git a/client/src/components/vehicles/VehicleList.js b/client/src/components/vehicles/VehicleList.js new file mode 100644 index 0000000..2cac248 --- /dev/null +++ b/client/src/components/vehicles/VehicleList.js @@ -0,0 +1,82 @@ +import React, {useState, useEffect} from "react"; +import { useDispatch } from "react-redux"; +import { useNavigate } from "react-router-dom"; +import { AuthService, VehicleService } from "../../services"; + +const VehicleList = () => { + const navigate = useNavigate(); + const dispatch = useDispatch(); + const [vehicles, setVehicles] = useState([]); + const [tag, setTag] = useState(''); + + useEffect(() => { + if (!AuthService.canAddOrEditVechiles()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + VehicleService.getAll().then((data) => + setVehicles(data.data) + ); + }, []); + + const redirectToAdmin = () => { + navigate(`/admin/customer-report`) + } + + const goToEdit = (id) => { + navigate(`/vehicles/edit/${id}?redirect=list`) + } + + + const goToView = (id) => { + navigate(`/vehicles/${id}`) + } + + + return ( + <> +
+
+
All Vehicles
+
+
+
+
+
Filter By Tag: setTag(e.currentTarget.value)}/>
+ + + + + + + + + + + + + + { + vehicles && vehicles.filter((item)=> item?.tag.toLowerCase().includes(tag.toLowerCase()))?.sort((a, b) => parseInt(a.vehicle_number)-parseInt(b.vehicle_number)).map(vehicle => + + + + + + + ) + } + +
Vehicle NumberTag NumberMileageStatusCapacity
{vehicle?.vehicle_number}{vehicle?.tag}{vehicle?.mileage}{vehicle?.status}{vehicle?.capacity} + {AuthService.canAddOrEditVechiles() && } + {AuthService.canViewVechiles() && } +
+ +
+
+ + ) +}; + +export default VehicleList; \ No newline at end of file diff --git a/client/src/components/vehicles/ViewVehicle.js b/client/src/components/vehicles/ViewVehicle.js new file mode 100644 index 0000000..f116509 --- /dev/null +++ b/client/src/components/vehicles/ViewVehicle.js @@ -0,0 +1,71 @@ +import React, {useState, useEffect} from "react"; +import { useNavigate, useParams } from "react-router-dom"; +import { AuthService, VehicleService } from "../../services"; + +const ViewVehicle = () => { + const navigate = useNavigate(); + + const urlParams = useParams(); + const [currentVehicle, setCurrentVehicle] = useState(undefined); + + const redirectTo = () => { + navigate(`/vehicles/list`) + } + + useEffect(() => { + if (!AuthService.canViewVechiles()) { + window.alert('You haven\'t login yet OR this user does not have access to this page. Please change an admin account to login.') + AuthService.logout(); + navigate(`/login`); + } + if (!currentVehicle) { + VehicleService.getVehicle(urlParams.id).then((data) => { + setCurrentVehicle(data.data); + }) + } + }, []); + + return ( + <> +
+
+
View Vechile {currentVehicle?.vehicle_number}
+
+
+
+
+
Vehicle Number: {currentVehicle?.vehicle_number}
+
+
+
Tag: {currentVehicle?.tag}
+
+
+
GPS: {currentVehicle?.gps_tag}
+
+
+
EZPass: {currentVehicle?.ezpass}
+
+
+
Capacity: {currentVehicle?.capacity}
+
+
+
Mileage: {currentVehicle?.mileage}
+
+
+
Make: {currentVehicle?.make}
+
+
+
Model: {currentVehicle?.vehicle_model}
+
+
+
Year: {currentVehicle?.year}
+
+
+
Checklist: {currentVehicle?.checklist?.join(', ')}
+
+
+ + ); +}; + +export default ViewVehicle; \ No newline at end of file diff --git a/client/src/effects/customer.saga.js b/client/src/effects/customer.saga.js new file mode 100644 index 0000000..66b207b --- /dev/null +++ b/client/src/effects/customer.saga.js @@ -0,0 +1,52 @@ +import { takeEvery, all, call, put } from 'redux-saga/effects'; +import { customerSlice } from './../store'; +import { CustomerService } from './../services'; + +const { createCustomer, createCustomerFailure, updateCustomer, updateCustomerFailure, deleteCustomer, deleteCustomerFailure } = customerSlice.actions; + +function* createCustomerSaga(action) { + try { + const customer = yield call(CustomerService.createNewCustomer, action.payload.data); + const client = yield call(CustomerService.createNewClient, action.payload.dataForLegacy); + if (action.payload.avatar) { + if (customer?.data?.id) { + CustomerService.uploadAvatar(customer.data.id, action.payload.avatar) + } + } + } catch(ex) { + yield put(createCustomerFailure(ex)); + } + } + +function* updateCustomerSaga(action) { + try { + yield call(CustomerService.updateCustomer, action.payload.id, action.payload.data); + // const targetClients = yield call(CustomerService.getClientsByNameOrEmail, action.payload.currentCustomer.email); + // console.log(targetClients.data); + // if (targetClients?.data?.length > 0) { + // yield call(CustomerService.updateClient, targetClients?.data[0]?.id, action.payload.dataForLegacy); + // } + if (action.payload.avatar) { + CustomerService.uploadAvatar(action.payload.id, action.payload.avatar) + } + } catch(ex) { + yield put(updateCustomerFailure(ex)); + } +} + +function* deleteCustomerSaga(action) { + try { + yield call(CustomerService.deleteCustomer, action.payload.id, action.payload.data); + // yield call(CustomerService.deleteCustomer, action.payload.id, action.payload.dataForLegacy) + } catch(ex) { + yield put(deleteCustomerFailure(ex)); + } +} + +export function* customerEffect() { + yield all([ + yield takeEvery(createCustomer.type, createCustomerSaga), + yield takeEvery(updateCustomer.type, updateCustomerSaga), + yield takeEvery(deleteCustomer.type, deleteCustomerSaga) + ]) +}; \ No newline at end of file diff --git a/client/src/effects/driver.saga.js b/client/src/effects/driver.saga.js new file mode 100644 index 0000000..f507baf --- /dev/null +++ b/client/src/effects/driver.saga.js @@ -0,0 +1,60 @@ +import { takeEvery, all, call, put } from 'redux-saga/effects'; +import { driverSlice } from './../store'; +import { DriverService } from './../services'; + +const { createDriver, createDriverFailure, fetchAllDrivers, fetchAllDriversSuccess, fetchAllDriversFailure, updateDriver, updateDriverFailure, deleteDriver, deleteDriverFailure } = driverSlice.actions; + +function* fetchDriversSaga() { + try { + const drivers = (yield call(DriverService.getAllActiveDrivers, 'driver')).data; + yield put(fetchAllDriversSuccess(drivers)); + } catch(ex) { + yield put(fetchAllDriversFailure(ex)); + } +} + +function* createDriverSaga(action) { + try { + yield call(DriverService.createNewDriver, action.payload.data); + const drivers = (yield call(DriverService.getAllActiveDrivers, 'driver')).data; + yield put(fetchAllDriversSuccess(drivers)); + yield call(DriverService.createNewDriverAsStaff, action.payload.data) + } catch(ex) { + yield put(createDriverFailure(ex)); + } + } + +function* updateDriverSaga(action) { + try { + yield call(DriverService.updateDriver, action.payload.id, action.payload.data); + const drivers = (yield call(DriverService.getAllActiveDrivers, 'driver')).data; + yield put(fetchAllDriversSuccess(drivers)); + const existedStaffs = (yield DriverService.getStaffsByNameOrEmail(action.payload.currentEmployee.email)).data; + if (existedStaffs && existedStaffs.length > 0) { + const staffData = Object.assign({}, action.payload.data ); + delete staffData.address; + yield call(DriverService.updateDriverInStaff(existedStaffs[0].id, staffData)) + } + } catch(ex) { + yield put(updateDriverFailure(ex)); + } +} + +function* deleteDriverSaga(action) { + try { + yield call(DriverService.deleteDriver, action.payload.id, action.payload.data); + const drivers = (yield call(DriverService.getAllActiveDrivers, 'driver')).data; + yield put(fetchAllDriversSuccess(drivers)); + } catch(ex) { + yield put(updateDriverFailure(ex)); + } +} + +export function* driverEffect() { + yield all([ + yield takeEvery(fetchAllDrivers.type, fetchDriversSaga), + yield takeEvery(createDriver.type, createDriverSaga), + yield takeEvery(updateDriver.type, updateDriverSaga), + yield takeEvery(deleteDriver.type, deleteDriverSaga) + ]) +}; \ No newline at end of file diff --git a/client/src/effects/employee.saga.js b/client/src/effects/employee.saga.js new file mode 100644 index 0000000..b7831bb --- /dev/null +++ b/client/src/effects/employee.saga.js @@ -0,0 +1,51 @@ +import { takeEvery, all, call, put } from 'redux-saga/effects'; +import { employeeSlice } from './../store'; +import { EmployeeService, DriverService } from './../services'; + +const { createEmployee, createEmployeeFailure, updateEmployee, updateEmployeeFailure, deleteEmployee, deleteEmployeeFailure } = employeeSlice.actions; + + + +function* createEmployeeSaga(action) { + try { + yield call(EmployeeService.createNewEmployee, action.payload.data); + if (action.payload.data.roles.includes('driver')) { + yield call(DriverService.createNewDriverAsStaff, action.payload.data) + } + } catch(ex) { + yield put(createEmployeeFailure(ex)); + } + } + +function* updateEmployeeSaga(action) { + try { + yield call(EmployeeService.updateEmployee, action.payload.id, action.payload.data); + if (action.payload?.currentEmployee.roles.includes('driver')) { + const existedStaffs = (yield DriverService.getStaffsByNameOrEmail(action.payload.currentEmployee.email)).data; + if (existedStaffs && existedStaffs.length > 0) { + const staffData = Object.assign({}, action.payload.data ); + delete staffData.address; + yield call(DriverService.updateDriverInStaff(existedStaffs[0].id, staffData)) + } + } + + } catch(ex) { + yield put(updateEmployeeFailure(ex)); + } +} + +function* deleteEmployeeSaga(action) { + try { + yield call(EmployeeService.deleteEmployee, action.payload.id, action.payload.data); + } catch(ex) { + yield put(updateEmployeeFailure(ex)); + } +} + +export function* employeeEffect() { + yield all([ + yield takeEvery(createEmployee.type, createEmployeeSaga), + yield takeEvery(updateEmployee.type, updateEmployeeSaga), + yield takeEvery(deleteEmployee.type, deleteEmployeeSaga) + ]) +}; \ No newline at end of file diff --git a/client/src/effects/index.js b/client/src/effects/index.js new file mode 100644 index 0000000..aaa1f06 --- /dev/null +++ b/client/src/effects/index.js @@ -0,0 +1,7 @@ +export * from './user.saga'; +export * from './trans-routes.saga'; +export * from './driver.saga'; +export * from './vehicle.saga'; +export * from './trans-route-templates.saga'; +export * from './customer.saga'; +export { rootSaga as default } from './root.saga'; \ No newline at end of file diff --git a/client/src/effects/root.saga.js b/client/src/effects/root.saga.js new file mode 100644 index 0000000..c553b4c --- /dev/null +++ b/client/src/effects/root.saga.js @@ -0,0 +1,20 @@ +import { all } from "redux-saga/effects"; +import { userEffect } from "./user.saga"; +import { transRoutesEffect } from './trans-routes.saga'; +import { driverEffect } from './driver.saga'; +import { vehicleEffect } from "./vehicle.saga"; +import { employeeEffect} from './employee.saga'; +import { customerEffect } from "./customer.saga"; +import { transRoutesTemplateEffect } from "./trans-route-templates.saga"; + +export function* rootSaga() { + yield all([ + userEffect(), + transRoutesEffect(), + driverEffect(), + vehicleEffect(), + employeeEffect(), + transRoutesTemplateEffect(), + customerEffect() + ]); +} \ No newline at end of file diff --git a/client/src/effects/trans-route-templates.saga.js b/client/src/effects/trans-route-templates.saga.js new file mode 100644 index 0000000..219eeb8 --- /dev/null +++ b/client/src/effects/trans-route-templates.saga.js @@ -0,0 +1,55 @@ +import { takeEvery, all, call, put } from 'redux-saga/effects'; +import { transRouteTemplatesSlice } from './../store'; +import { RouteTemplateService } from './../services'; + +const { + fetchAllRouteTemplates, + fetchAllRouteTemplatesSuccess, + fetchAllRouteTemplatesFailure, + updateRouteTemplate, + updateRouteTemplateFailure, + createRouteTemplate, + createRouteTemplateFailure +} = transRouteTemplatesSlice.actions; + + +function* fetchRouteTemplatesSaga() { + try { + const routes = (yield call(RouteTemplateService.getAll)).data; + yield put(fetchAllRouteTemplatesSuccess(routes)); + } catch(ex) { + yield put(fetchAllRouteTemplatesFailure(ex)); + } +} + +function* updateRouteTemplateSaga(action) { + try { + yield call(RouteTemplateService.updateRouteTemplate, action.payload.id, action.payload.data); + const routeTemplates = (yield call(RouteTemplateService.getAll)).data; + yield put(fetchAllRouteTemplatesSuccess(routeTemplates)); + if (action.payload.callback) { + action.payload.callack(); + } + } catch(ex) { + yield put(updateRouteTemplateFailure(ex)); + } +} + +function* createRouteTemplateSaga(action) { + try { + yield call(RouteTemplateService.createNewRouteTemplate, action.payload.data); + const routeTemplates = (yield call(RouteTemplateService.getAll)).data; + yield put(fetchAllRouteTemplatesSuccess(routeTemplates)); + + } catch(ex) { + yield put(createRouteTemplateFailure(ex)); + } +} + +export function* transRoutesTemplateEffect() { + yield all([ + yield takeEvery(fetchAllRouteTemplates.type, fetchRouteTemplatesSaga), + yield takeEvery(updateRouteTemplate.type, updateRouteTemplateSaga), + yield takeEvery(createRouteTemplate.type, createRouteTemplateSaga) + ]) +}; \ No newline at end of file diff --git a/client/src/effects/trans-routes.saga.js b/client/src/effects/trans-routes.saga.js new file mode 100644 index 0000000..57122a4 --- /dev/null +++ b/client/src/effects/trans-routes.saga.js @@ -0,0 +1,160 @@ +import { takeEvery, all, call, put } from 'redux-saga/effects'; +import { transRoutesSlice } from './../store'; +import { TransRoutesService } from './../services'; +import moment from 'moment'; + +const { + fetchAllRoutes, + fetchAllRoutesSuccess, + fetchAllRoutesFailure, + updateRoute, + updateRouteFailure, + fetchAllTomorrowRoutes, + fetchAllTomorrowRoutesSuccess, + fetchAllTomorrowRoutesFailure, + createRoute, + createRouteFailure, + fetchAllHisotryRoutesSuccess, + fetchAllHistoryRoutes, + fetchAllHistoryRoutesFailure, + fetchAllBreakfastRecords, + fetchAllBreakfastRecordsSuccess, + fetchAllBreakfastRecordsFailure, + fetchAllLunchRecords, + fetchAllLunchRecordsSuccess, + fetchAllLunchRecordsFailure, + fetchAllSnackRecords, + fetchAllSnackRecordsSuccess, + fetchAllSnackRecordsFailure +} = transRoutesSlice.actions; + +function* fetchHistoryRoutesSaga(action) { + try { + const dateText = action.payload.dateText; + const routes = (yield call(TransRoutesService.getAll, dateText)).data; + yield put(fetchAllHisotryRoutesSuccess(routes)); + } catch(ex) { + yield put(fetchAllHistoryRoutesFailure(ex)); + } +} + +function* fetchRoutesSaga() { + try { + const date = new Date(); + const dateText = ((date.getMonth() > 8) ? (date.getMonth() + 1) : ('0' + (date.getMonth() + 1))) + '/' + ((date.getDate() > 9) ? date.getDate() : ('0' + date.getDate())) + '/' + date.getFullYear(); + const routes = (yield call(TransRoutesService.getAll, dateText)).data; + yield put(fetchAllRoutesSuccess(routes)); + } catch(ex) { + yield put(fetchAllRoutesFailure(ex)); + } +} + +function* fetchBreakfastsRecordsSaga() { + try { + const breakfasts = (yield call(TransRoutesService.getAllBreakfastRecords, moment(new Date()).format('MM/DD/YYYY')))?.data; + yield put(fetchAllBreakfastRecordsSuccess(breakfasts)); + } catch(ex) { + yield put(fetchAllBreakfastRecordsFailure(ex)); + } +} + +function* fetchLunchRecordsSaga() { + try { + const lunches = (yield call(TransRoutesService.getAllLunchRecords, moment(new Date()).format('MM/DD/YYYY')))?.data; + yield put(fetchAllLunchRecordsSuccess(lunches)); + } catch(ex) { + yield put(fetchAllLunchRecordsFailure(ex)); + } +} + +function* fetchSnackRecordsSaga() { + try { + const snacks = (yield call(TransRoutesService.getAllSnackRecords, moment(new Date()).format('MM/DD/YYYY')))?.data; + yield put(fetchAllSnackRecordsSuccess(snacks)); + } catch(ex) { + yield put(fetchAllSnackRecordsFailure(ex)); + } +} + +// Tomorrow Doesn't really mean 'tomorrow' but maybe the selected future date. +function* fetchAllTomorrowRoutesSaga(action) { + try { + const date = new Date(); + date.setDate(date.getDate() + 1); + const dateText = action.payload.dateText ? action.payload.dateText : ((date.getMonth() > 8) ? (date.getMonth() + 1) : ('0' + (date.getMonth() + 1))) + '/' + ((date.getDate() > 9) ? date.getDate() : ('0' + date.getDate())) + '/' + date.getFullYear(); + const routes = (yield call(TransRoutesService.getAll, dateText)).data; + yield put(fetchAllTomorrowRoutesSuccess(routes)); + } catch(ex) { + yield put(fetchAllTomorrowRoutesFailure(ex)); + } +} + +function* updateRouteSaga(action) { + try { + yield call(TransRoutesService.updateRoute, action.payload.id, action.payload.data); + // yield call(TransRoutesService.updateInProgress, action.payload.data); + if (action.payload.dateText) { + const routes = (yield call(TransRoutesService.getAll, action.payload.dateText)).data; + if (action.payload.fromSchedule) { + yield put(fetchAllTomorrowRoutesSuccess(routes)); + } else { + yield put(fetchAllHisotryRoutesSuccess(routes)); + } + + } else { + const date = new Date(); + const dateText = ((date.getMonth() > 8) ? (date.getMonth() + 1) : ('0' + (date.getMonth() + 1))) + '/' + ((date.getDate() > 9) ? date.getDate() : ('0' + date.getDate())) + '/' + date.getFullYear(); + const routes = (yield call(TransRoutesService.getAll, dateText)).data; + yield put(fetchAllRoutesSuccess(routes)); + date.setDate(date.getDate() + 1); + const tmrDateText = ((date.getMonth() > 8) ? (date.getMonth() + 1) : ('0' + (date.getMonth() + 1))) + '/' + ((date.getDate() > 9) ? date.getDate() : ('0' + date.getDate())) + '/' + date.getFullYear(); + const routesTmr = (yield call(TransRoutesService.getAll, tmrDateText)).data; + yield put(fetchAllTomorrowRoutesSuccess(routesTmr)); + } + if (action.payload.callback) { + action.payload.callback(); + } + } catch(ex) { + yield put(updateRouteFailure(ex)); + window.alert(`Fail to Update Route: ${typeof ex === 'string' ? ex : JSON.stringify(ex)}` ) + } +} + +function* createRouteSaga(action) { + try { + yield call(TransRoutesService.createNewRoute, action.payload.data); + if (action.payload.fetchToday) { + const date = new Date(); + const dateText = ((date.getMonth() > 8) ? (date.getMonth() + 1) : ('0' + (date.getMonth() + 1))) + '/' + ((date.getDate() > 9) ? date.getDate() : ('0' + date.getDate())) + '/' + date.getFullYear(); + const routes = (yield call(TransRoutesService.getAll, dateText)).data; + yield put(fetchAllRoutesSuccess(routes)); + } + if (action.payload.fetchTomorrow) { + const date = new Date(); + date.setDate(date.getDate() + 1); + const dateText = ((date.getMonth() > 8) ? (date.getMonth() + 1) : ('0' + (date.getMonth() + 1))) + '/' + ((date.getDate() > 9) ? date.getDate() : ('0' + date.getDate())) + '/' + date.getFullYear(); + const routes = (yield call(TransRoutesService.getAll, dateText)).data; + yield put(fetchAllTomorrowRoutesSuccess(routes)); + } + if (action.payload.fetchDate) { + const routes = (yield call(TransRoutesService.getAll, action.payload.fetchDate)).data; + yield put(fetchAllTomorrowRoutesSuccess(routes)); + } + } catch(ex) { + yield put(createRouteFailure(ex)); + window.alert(`Fail to Create Route: ${typeof ex === 'string' ? ex : JSON.stringify(ex)}` ) + } +} + +export function* transRoutesEffect() { + yield all([ + yield takeEvery(fetchAllRoutes.type, fetchRoutesSaga), + yield takeEvery(updateRoute.type, updateRouteSaga), + yield takeEvery(fetchAllTomorrowRoutes.type, fetchAllTomorrowRoutesSaga), + yield takeEvery(createRoute.type, createRouteSaga), + yield takeEvery(fetchAllHistoryRoutes.type, fetchHistoryRoutesSaga), + yield takeEvery(fetchAllBreakfastRecords.type, fetchBreakfastsRecordsSaga), + yield takeEvery(fetchAllLunchRecords.type, fetchLunchRecordsSaga), + yield takeEvery(fetchAllSnackRecords.type, fetchSnackRecordsSaga) + ]) +}; \ No newline at end of file diff --git a/client/src/effects/user.saga.js b/client/src/effects/user.saga.js new file mode 100644 index 0000000..dfc80d4 --- /dev/null +++ b/client/src/effects/user.saga.js @@ -0,0 +1,20 @@ +import { takeEvery, all, call, put } from 'redux-saga/effects'; +import { userSlice } from './../store'; +import { UserService } from './../services'; + +const { fetchAllUsers, fetchAllUsersSuccess, fetchAllUsersFailure } = userSlice.actions; + +function* fetchUsersSaga() { + try { + const users = (yield call(UserService.getAll)).data; + yield put(fetchAllUsersSuccess(users)); + } catch(ex) { + yield put(fetchAllUsersFailure(ex)); + } +} + +export function* userEffect() { + yield all([ + yield takeEvery(fetchAllUsers.type, fetchUsersSaga) + ]) +}; \ No newline at end of file diff --git a/client/src/effects/vehicle.saga.js b/client/src/effects/vehicle.saga.js new file mode 100644 index 0000000..3099595 --- /dev/null +++ b/client/src/effects/vehicle.saga.js @@ -0,0 +1,59 @@ +import { takeEvery, all, call, put } from 'redux-saga/effects'; +import { vehicleSlice } from '../store'; +import { VehicleService } from '../services'; + +const { createVehicle, createVehicleFailure, fetchAllVehicles, fetchAllVehiclesSuccess, fetchAllVehiclesFailure, updateVehicle, updateVehicleFailure, deleteVehicle, deleteVehicleFailure } = vehicleSlice.actions; + +function* fetchVehiclesSaga() { + try { + const vehicles = (yield call(VehicleService.getAll)).data; + yield put(fetchAllVehiclesSuccess(vehicles)); + } catch(ex) { + yield put(fetchAllVehiclesFailure(ex?.response?.data?.message || "error")); + } +} + +function* updateVehicleSaga(action) { + try { + yield call(VehicleService.updateVehicles, action.payload.id, action.payload.data); + const vehicles = (yield call(VehicleService.getAll)).data; + yield put(fetchAllVehiclesSuccess(vehicles)); + if (action.payload.redirectFun) { + action.payload.redirectFun(); + } + } catch(ex) { + yield put(updateVehicleFailure(ex?.response?.data?.message || "error")); + } +} + +function* deleteVehicleSaga(action) { + try { + yield call(VehicleService.deleteVehicles, action.payload.id, action.payload.data); + const vehicles = (yield call(VehicleService.getAll)).data; + yield put(fetchAllVehiclesSuccess(vehicles)); + } catch(ex) { + yield put(deleteVehicleFailure(ex?.response?.data?.message || "error")); + } +} + +function* createVehicleSaga(action) { + try { + yield call(VehicleService.createNewVehicles, action.payload.data); + const vehicles = (yield call(VehicleService.getAll)).data; + yield put(fetchAllVehiclesSuccess(vehicles)); + if (action.payload.redirectFun) { + action.payload.redirectFun(); + } + } catch(ex) { + yield put(createVehicleFailure(ex?.response?.data?.message || "error")); + } +} + +export function* vehicleEffect() { + yield all([ + yield takeEvery(fetchAllVehicles.type, fetchVehiclesSaga), + yield takeEvery(updateVehicle.type, updateVehicleSaga), + yield takeEvery(createVehicle.type, createVehicleSaga), + yield takeEvery(deleteVehicle.type, deleteVehicleSaga) + ]) +}; \ No newline at end of file diff --git a/client/src/http-common.js b/client/src/http-common.js new file mode 100644 index 0000000..54060fe --- /dev/null +++ b/client/src/http-common.js @@ -0,0 +1,10 @@ +import axios from "axios"; +export default axios.create({ + baseURL: (window.location.hostname.includes('worldshine.mayo.llc') || window.location.hostname.includes('site')) ? "https://worldshine.mayo.llc/api" : ((window.location.hostname.includes('worldshine3.mayo.llc') || window.location.hostname.includes('site3')) ? "https://worldshine3.mayo.llc/api" : "https://worldshine2.mayo.llc/api"), + // for Test site only: + //baseURL: (window.location.hostname.includes('ws1') ||window.location.hostname.includes('localhost') || window.location.hostname.includes('site1')) ? "https://ws1.mayosolution.com/api" : ((window.location.hostname.includes('ws3') || window.location.hostname.includes('site3')) ? "https://ws3.mayosolution.com/api" : "https://ws2.mayosolution.com/api"), + // baseURL: "http://localhost:8080/api", + headers: { + "Content-type": "application/json" + } +}); \ No newline at end of file diff --git a/client/src/index.css b/client/src/index.css new file mode 100644 index 0000000..ec2585e --- /dev/null +++ b/client/src/index.css @@ -0,0 +1,13 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} diff --git a/client/src/index.js b/client/src/index.js new file mode 100644 index 0000000..628c6d9 --- /dev/null +++ b/client/src/index.js @@ -0,0 +1,27 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import './index.css'; +import App from './App'; +import reportWebVitals from './reportWebVitals'; + +import { Provider } from 'react-redux'; +import store from './store/store'; +import setupInterceptors from "./services/setupInterceptor"; + + +const root = ReactDOM.createRoot( + document.getElementById('root') +); + +root.render( + + + +); +setupInterceptors(); + + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/client/src/logo.svg b/client/src/logo.svg new file mode 100644 index 0000000..9dfc1c0 --- /dev/null +++ b/client/src/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/reportWebVitals.js b/client/src/reportWebVitals.js new file mode 100644 index 0000000..5253d3a --- /dev/null +++ b/client/src/reportWebVitals.js @@ -0,0 +1,13 @@ +const reportWebVitals = onPerfEntry => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/client/src/services/AuthService.js b/client/src/services/AuthService.js new file mode 100644 index 0000000..80e904e --- /dev/null +++ b/client/src/services/AuthService.js @@ -0,0 +1,125 @@ +import http from "../http-common"; +import {EMPLOYEE_ROLES} from "../shared"; +const login = (data) => { + return http.post('/auth/login', data); +}; + +const logout = (data) => { + localStorage.removeItem('user'); + localStorage.removeItem('token'); + window.location.href="/login"; +}; + +const isAdmin = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && roles?.includes('admin'); +} + +// const isDispatcher = () => { +// const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; +// return roles && (roles?.includes('dispatcher') || roles?.includes('admin')); +// } + +const canCreateOrEditDrivers = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && (roles?.includes(EMPLOYEE_ROLES.DRIVER_EDITOR) || roles?.includes(EMPLOYEE_ROLES.ADMIN)); +} + +const canViewDrivers = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && (roles?.includes(EMPLOYEE_ROLES.DRIVER_VIEWER) || roles?.includes(EMPLOYEE_ROLES.ADMIN)); +} + +const canViewEmployees = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && (roles?.includes(EMPLOYEE_ROLES.EMPLOYEE_VIEWER) || roles?.includes(EMPLOYEE_ROLES.ADMIN)); +} + +const canAddOrEditEmployees = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && (roles?.includes(EMPLOYEE_ROLES.EMPLOYEE_EDITOR) || roles?.includes(EMPLOYEE_ROLES.ADMIN)); +} + +const canAddOrEditRoutes = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && (roles?.includes(EMPLOYEE_ROLES.ROUTE_EDITOR) || roles?.includes(EMPLOYEE_ROLES.ADMIN)); +} + +const canViewRoutes = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && (roles?.includes(EMPLOYEE_ROLES.ROUTE_VIEWER) || roles?.includes(EMPLOYEE_ROLES.ADMIN)); +} + +const canViewVechiles = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && (roles?.includes(EMPLOYEE_ROLES.VEHICLE_VIEWER) || roles?.includes(EMPLOYEE_ROLES.ADMIN)); +} + +const canAddOrEditVechiles = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && (roles?.includes(EMPLOYEE_ROLES.VEHICLE_EDITOR) || roles?.includes(EMPLOYEE_ROLES.ADMIN)); +} + +const canViewCustomers = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && (roles?.includes(EMPLOYEE_ROLES.CUSTOMER_VIEWER) || roles?.includes(EMPLOYEE_ROLES.ADMIN)); +} + +const canAddOrEditCustomers = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && (roles?.includes(EMPLOYEE_ROLES.CUSTOMER_EDITOR) || roles?.includes(EMPLOYEE_ROLES.ADMIN)); +} + +const canViewAttendance = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && (roles?.includes(EMPLOYEE_ROLES.ATTENDANCE_VIEWER) || roles?.includes(EMPLOYEE_ROLES.ADMIN)); +} + +const canAddOrEditAttendance = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && (roles?.includes(EMPLOYEE_ROLES.ATTENDANCE_EDITOR) || roles?.includes(EMPLOYEE_ROLES.ADMIN)); +} + +const canAddOrEditMedical = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && (roles?.includes(EMPLOYEE_ROLES.MEDICAL_RESERVATION_INFO_EDITOR) || roles?.includes(EMPLOYEE_ROLES.MEDICAL_RESERVATION_INFO_VIEWER)); +} + +const canAddOrEditMedicalSchedule = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && (roles?.includes(EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_EDITOR) || roles?.includes(EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_VIEWER)); +} + +const canAddOrEditResources = () => { + const roles = localStorage.getItem('user') && JSON.parse(localStorage.getItem('user'))?.roles; + return roles && (roles?.includes(EMPLOYEE_ROLES.RESOURCE_LIST_EDITOR) || roles?.includes(EMPLOYEE_ROLES.RESOURCE_LIST_VIEWER)); +} + +const canAccessLegacySystem = () => { + return isAdmin() || canAddOrEditAttendance() || canAddOrEditMedical() || canAddOrEditMedicalSchedule() || canAddOrEditResources(); +} + +const getLocalAccessToken = () => { + return localStorage.getItem('token'); +} + + +export const AuthService = { + login, + logout, + isAdmin, + canCreateOrEditDrivers, + getLocalAccessToken, + canViewDrivers, + canViewEmployees, + canAddOrEditEmployees, + canAddOrEditRoutes, + canViewRoutes, + canViewVechiles, + canAddOrEditVechiles, + canViewCustomers, + canAddOrEditCustomers, + canAddOrEditAttendance, + canViewAttendance, + canAccessLegacySystem +}; diff --git a/client/src/services/CenterPhoneService.js b/client/src/services/CenterPhoneService.js new file mode 100644 index 0000000..01f4b50 --- /dev/null +++ b/client/src/services/CenterPhoneService.js @@ -0,0 +1,24 @@ +import http from "../http-common"; +const getAll = (activated) => { + const params = {}; + if (activated !== undefined ) { + params.activated = activated; + } + return http.get("/phones"); +}; +const updateCenterPhone = (id, data) => { + return http.put(`/phones/${id}`, data) +}; +const createNewCenterPhone = (data) => { + return http.post('/phones', data); +}; +const getCenterPhone = (id) => { + return http.get(`/phones/${id}`); +}; + +export const CenterPhoneService = { + getAll, + updateCenterPhone, + createNewCenterPhone, + getCenterPhone +}; diff --git a/client/src/services/CustomerService.js b/client/src/services/CustomerService.js new file mode 100644 index 0000000..385435e --- /dev/null +++ b/client/src/services/CustomerService.js @@ -0,0 +1,84 @@ +import http from "../http-common"; +const getAllActiveCustomers = () => { + return http.get('/customers/active'); +}; + +const getAllCustomers = () => { + return http.get('/customers'); +}; + +const createNewCustomer = (data) => { + data.status = 'active'; + return http.post('/customers', data); +}; + +const createNewClient = (data) => { + data.status = 'active'; + return http.post('/clients', data); +} + +const updateCustomer = (id, data) => { + return http.put(`/customers/${id}`, data); +} + +const updateClient = (id, data) => { + data.status = 'active'; + return http.put(`/clients/${id}`, data); +} + +const deleteCustomer = (id, data) => { + data.status = 'inactive'; + return http.put(`/customers/${id}`, data); +} + +const deleteClient = (id, data) => { + data.status = 'inactive'; + return http.put(`/clients/${id}`, data); +} + +const uploadAvatar = (filename, data) => { + return http.post(`/files/upload/${filename}`, data) +} + +const getAvatar = (filename) => { + return http.get(`/files/${filename}`); +} + +const deleteFile = (data) => { + return http.post(`/files/delete`, data); +} + +const getCustomersByNameOrEmail = (nameOrEmail) => { + return http.get(`/customers/search`, {params: {nameOrEmail}}); +} + +const getClientsByNameOrEmail = (nameOrEmail) => { + return http.get(`/clients/search`, {params: {nameOrEmail}}); +} + +const getCustomer = (id) => { + return http.get(`customers/${id}`); +} + +const getClient = (id) => { + return http.get(`clients/${id}`); +} + + +export const CustomerService = { + getAllActiveCustomers, + uploadAvatar, + getAvatar, + deleteFile, + createNewCustomer, + updateCustomer, + deleteCustomer, + getCustomersByNameOrEmail, + getCustomer, + getAllCustomers, + createNewClient, + updateClient, + deleteClient, + getClientsByNameOrEmail, + getClient +}; diff --git a/client/src/services/DispatcherService.js b/client/src/services/DispatcherService.js new file mode 100644 index 0000000..fcd1b50 --- /dev/null +++ b/client/src/services/DispatcherService.js @@ -0,0 +1,15 @@ +import http from "../http-common"; +import { EMPLOYEE_TITLE_CN, EMPLOYEE_TITLE_ROLES_MAP } from "../shared"; + +const createNewDispatcher = (data) => { + data.roles = EMPLOYEE_TITLE_ROLES_MAP.DRIVER; + data.status = 'inactive'; + data.title = 'DRIVER'; + data.title_cn = EMPLOYEE_TITLE_CN.DRIVER; + return http.post('/employees', data); +}; + + +export const DispatcherService = { + createNewDispatcher +}; diff --git a/client/src/services/DriverService.js b/client/src/services/DriverService.js new file mode 100644 index 0000000..d61a74b --- /dev/null +++ b/client/src/services/DriverService.js @@ -0,0 +1,72 @@ +import http from "../http-common"; +import { EMPLOYEE_TITLE, EMPLOYEE_TITLE_CN, EMPLOYEE_TITLE_ROLES_MAP } from "../shared"; +const getAllActiveDrivers = (role, status) => { + const params = {}; + if (role) { + params.role = role.toLowerCase(); + } + if (status) { + params.status = status; + } + return http.get('/employees', { + params + }); +}; + +const createNewDriver = (data) => { + data.roles = EMPLOYEE_TITLE_ROLES_MAP.DRIVER; + data.status = 'active'; + data.title = 'DRIVER'; + data.title_cn = EMPLOYEE_TITLE_CN.DRIVER; + return http.post('/employees', data); +}; + +const updateDriver = (id, data) => { + if (!data.roles) { + data.roles = ['driver']; + } else { + if (data.roles && !data.roles.includes('driver')) { + data.roles.push('dirver'); + } + } + return http.put(`/employees/${id}`, data); +} + +const deleteDriver = (id, data) => { + data.status = 'inactive'; + return http.put(`/employees/${id}`, data); +} + +const createNewDriverAsStaff = (data) => { + data.roles = EMPLOYEE_TITLE_ROLES_MAP.DRIVER; + data.status = 'active'; + data.title = 'DRIVER'; + data.title_cn = EMPLOYEE_TITLE_CN.DRIVER; + data.tags = 'can_drive'; + return http.post('/staffs', data); +} + +const updateDriverInStaff = (id, data) => { + if (!data.roles) { + data.roles = ['driver']; + } else { + if (data.roles && !data.roles.includes('driver')) { + data.roles.push('dirver'); + } + } + return http.put(`/staffs/${id}`, data); +} + +const getStaffsByNameOrEmail = (nameOrEmail) => { + return http.get(`/staffs/search`, {params: {nameOrEmail}}); +} + +export const DriverService = { + getAllActiveDrivers, + createNewDriver, + updateDriver, + deleteDriver, + createNewDriverAsStaff, + updateDriverInStaff, + getStaffsByNameOrEmail +}; diff --git a/client/src/services/EmployeeService.js b/client/src/services/EmployeeService.js new file mode 100644 index 0000000..fedf217 --- /dev/null +++ b/client/src/services/EmployeeService.js @@ -0,0 +1,63 @@ +import http from "../http-common"; +const getAllEmployees = (role, status) => { + const params = {}; + if (role) { + params.roles = role; + } + if (status) { + params.status = status; + } + return http.get('/employees', { + params + }); +}; + +const getEmployee = (id) => { + return http.get(`/employees/${id}`); +} + +const createNewEmployee = (data) => { + data.status = 'active'; + return http.post('/employees', data); +}; + +const updateEmployee = (id, data) => { + return http.put(`/employees/${id}`, data); +} + +const deleteEmployee = (id, data) => { + data.status = 'inactive'; + return http.put(`/employees/${id}`, data); +} + +const validatePassword = (password = '') => { + const lowercaseRegex = /[a-z]/; + const uppercaseRegex = /[A-Z]/; + const specialCharRegex = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/; + if (password?.length < 8) { + return false; + } + + if (!lowercaseRegex.test(password)) { + return false; // Missing lowercase letter + } + + if (!uppercaseRegex.test(password)) { + return false; // Missing uppercase letter + } + + if (!specialCharRegex.test(password)) { + return false; // Missing special character + } + + return true; // Password meets criteria +} + +export const EmployeeService = { + getAllEmployees, + createNewEmployee, + updateEmployee, + deleteEmployee, + getEmployee, + validatePassword +}; diff --git a/client/src/services/EventRequestService.js b/client/src/services/EventRequestService.js new file mode 100644 index 0000000..f7b22d3 --- /dev/null +++ b/client/src/services/EventRequestService.js @@ -0,0 +1,57 @@ +import http from "../http-common"; + +const getAllEventRequests = (params) => { + return http.get('/event-requests', { params}); +}; + +const createNewEventRequest = (data) => { + data.status = 'active'; + return http.post('/event-requests', data); +}; + +const updateEventRequest = (id, data) => { + return http.put(`/event-requests/${id}`, data); +} + +const deleteEventRequest = (id) => { + return http.delete(`/event-requests/${id}`) +} + +const sourceList = [ + { + value: 'byClient', + label: '老人要求 By Client' + }, + { + value: 'byDoctor', + label: '医生要求 By Doctor' + }, + { + value: 'returnTkt', + label: '回诊单 Return Tkt' + }, + { + value: 'bySocialWorker', + label: '社工部 By Social Worker' + }, + { + value: 'byTransportation', + label: '交通部 By Transportation' + }, + { + value: 'byWsNursing', + label: '护士站 By WS Nursing' + }, + { + value: 'forDoctorReport', + label: '要报告 For Doctor Report' + }, +] + +export const EventRequestsService = { + createNewEventRequest, + getAllEventRequests, + updateEventRequest, + deleteEventRequest, + sourceList +}; \ No newline at end of file diff --git a/client/src/services/EventsService.js b/client/src/services/EventsService.js new file mode 100644 index 0000000..602c038 --- /dev/null +++ b/client/src/services/EventsService.js @@ -0,0 +1,190 @@ +import moment from "moment"; +import CenterPhoneList from "../components/center-phone/CenterPhoneList"; +import http from "../http-common"; + +const getAllEvents = (params) => { + return http.get('/events', { params}); +}; + +const getByCustomer = (params) => { + return http.get('/events/getByCustomer', {params}) +}; + +const createNewEvent = (data) => { + data.status = 'active'; + return http.post('/events', data); +}; + + +const updateEvent = (id, data) => { + return http.put(`/events/${id}`, data); +} + +const disableEvent = (id, data) => { + return http.put(`/events/${id}/disable`, data); +} + +const getEvent = (id) => { + return http.get(`/events/${id}`); +} + +const deleteEvent = (id) => { + return http.delete(`/events/${id}`) +} + +const assignTransportationToEvents = (data) => { + return http.post(`/events/assign`, data); +} + +const formatDate = (date) => { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const localDate = `${year}-${month}-${day}`; + return localDate; +}; + +const getTimeData = () => { + return http.get(`/timedata/get-by-condition1`, { + params: { + date: moment(new Date()).format('YYYY-MM-DD') + } + }); +} + +const getTransportationInfo = (eventsList, item, timeDocs = []) => { + const today = new Date(); + // const timeDocs = (await http.get(`/timedata/get-by-condition1`, { + // params: { + // date: moment(today).format('YYYY-MM-DD') + // } + // }))?.data; + + const futureEvents = eventsList.filter((d) => new Date(d.start_time) >= new Date(today.toLocaleDateString()) ); + const isFutureEvent = (event) => futureEvents.find((fe) => fe.id === event.id); + const futureEventsGroupMap = new Map(); + for (const e of futureEvents) { + const eDateString = new Date(e.start_time).toLocaleDateString(); + if (futureEventsGroupMap.has(eDateString)) { + futureEventsGroupMap.set(eDateString, [...futureEventsGroupMap.get(eDateString), e]) + } else { + futureEventsGroupMap.set(eDateString, [e]); + } + } + return { + maxTranslate1: timeDocs.find(timeDoc => moment(timeDoc.time).format('YYYY-MM-DD') == moment(item.start_time).format('YYYY-MM-DD'))?.translate1_number || 0, + maxTranslate2: timeDocs.find(timeDoc => moment(timeDoc.time).format('YYYY-MM-DD') == moment(item.start_time).format('YYYY-MM-DD'))?.translate2_number || 0, + maxResource: timeDocs.find(timeDoc => moment(timeDoc.time).format('YYYY-MM-DD') == moment(item.start_time).format('YYYY-MM-DD'))?.max_resource_number || 0, + totalTranslate1: futureEventsGroupMap.get(new Date(item?.start_time).toLocaleDateString())?.filter(e => e.interpreter === '1(译)')?.length, + totalTranslate2: futureEventsGroupMap.get(new Date(item?.start_time).toLocaleDateString())?.filter(e => e.interpreter === '2(译)')?.length, + totalResource: [... new Set(futureEventsGroupMap.get(new Date(item?.start_time).toLocaleDateString())?.map(e => e.source_uuid))]?.length, + isFutureEvent: isFutureEvent(item) + } +} + +const interpreterLevelOptions = [ + { + value: 'Checkin', + label: 'Checkin' + }, + { + value: 'Center', + label: 'Center', + }, + { + value: 'Family', + label: 'Family' + }, + { + value: 'Office', + label: 'Office inperson' + }, + { + value: 'Office(Phone)', + label: 'Office byphone' + } +]; + +const colorOptions = [ + { + value: 'red', + label: 'EyesOn' + }, + { + value: 'brown', + label: 'ByOwn', + }, + { + value: 'green', + label: 'Televisit' + }, + { + value: 'blue', + label: 'Default for ByCenter' + }, + { + value: 'black', + label: 'Cient Does Not Need to Go' + }, + { + value: 'purple', + label: 'Dropoff Only', + }, + { + value: 'gray', + label: 'Deleted Appt' + }, + { + value: 'orange', + label: 'Pickup Only' + }, + { + value: 'pink', + label: 'Translate(center)' + } +]; + + + +//const baseUrl = (window.location.hostname.includes('ws1') ||window.location.hostname.includes('localhost') || window.location.hostname.includes('site1')) ? "https://ws1.mayosolution.com/api" : ((window.location.hostname.includes('ws3') || window.location.hostname.includes('site3')) ? "https://ws3.mayosolution.com/api" : "https://ws2.mayosolution.com/api") +const baseUrl = (window.location.hostname.includes('worldshine.mayo.llc') ||window.location.hostname.includes('worldshine1') || window.location.hostname.includes('site1')) ? "https://worldshine.mayo.llc/api" : ((window.location.hostname.includes('worldshine3') || window.location.hostname.includes('site3')) ? "https://worldshine3.mayo.llc/api" : "https://worldshine2.mayo.llc/api") + +//const site = (window.location.hostname.includes('ws1') ||window.location.hostname.includes('localhost') || window.location.hostname.includes('site1')) ? 1 : ((window.location.hostname.includes('ws3') || window.location.hostname.includes('site3')) ? 3 : 2) +const site = (window.location.hostname.includes('worldshine.mayo.llc') ||window.location.hostname.includes('worldshine1') || window.location.hostname.includes('site1')) ? 1 : ((window.location.hostname.includes('worldshine3') || window.location.hostname.includes('site3')) ? 3 : 2) + +const generatePdf = (data) => { + window.open(`${baseUrl}/docs/get-pdfs?docTemplateName=med_notification&inputData=${encodeURIComponent(JSON.stringify(data))}`); +} + +const generateSheet = (data) => { + window.open(`${baseUrl}/sheets/get-sheets?excelTemplateName=visit_record_sheet&inputData=${encodeURIComponent(JSON.stringify(data))}`); +} + +const generateDoc = (data) => { + window.open(`${baseUrl}/docs/get-docs?docTemplateName=med_notification&inputData=${encodeURIComponent(JSON.stringify(data))}`); +} + +const generateSheetPDF = (data) => { + window.open(`${baseUrl}/sheets/get-pdf?excelTemplateName=visit_record_sheet&inputData=${encodeURIComponent(JSON.stringify(data))}`); +} + +export const EventsService = { + createNewEvent, + getAllEvents, + updateEvent, + disableEvent, + getEvent, + deleteEvent, + assignTransportationToEvents, + formatDate, + getTransportationInfo, + generatePdf, + generateSheet, + generateDoc, + generateSheetPDF, + getTimeData, + getByCustomer, + site, + interpreterLevelOptions, + colorOptions +}; diff --git a/client/src/services/MessageService.js b/client/src/services/MessageService.js new file mode 100644 index 0000000..127f34f --- /dev/null +++ b/client/src/services/MessageService.js @@ -0,0 +1,63 @@ +import http from "../http-common"; + +const getMessagesByGroupAndLanguage = (message_group, language) => { + const params = {}; + if (message_group) { + params.message_group = message_group; + } + if (language) { + params.language = language; + } + return http.get('/messages/search', { + params + }); +} + +const createMessage = (data) => { + return http.post('/messages', data); +}; + +const updateMessage = (id, data) => { + return http.put(`/messages/${id}`, data); +} + +const getMessage = (id) => { + return http.get(`/messages/${id}`); +} + +const getMessages = () => { + return http.get(`/messages`); +} + +const getSendMessageToken = () => { + return http.get(`/message-tokens`) +} + +const createMessageToken = (data) => { + return http.post('/message-tokens', data); +} + +const updateMessageToken = (id, data) => { + return http.put(`/message-tokens/${id}`, data); +} + +const sendMessage = (data) => { + return http.post(`/messages/public/send`, data); +} + +const getSentMessages = () => { + return http.get(`/messages/sent-messages/all`); +} + +export const MessageService = { + getMessage, + getMessages, + updateMessage, + createMessage, + getMessagesByGroupAndLanguage, + getSendMessageToken, + updateMessageToken, + createMessageToken, + sendMessage, + getSentMessages +}; diff --git a/client/src/services/ReportService.js b/client/src/services/ReportService.js new file mode 100644 index 0000000..61963a6 --- /dev/null +++ b/client/src/services/ReportService.js @@ -0,0 +1,43 @@ +import http from "../http-common"; +const getReportsByDateAndType = (date, type) => { + const params = {}; + if (date) { + params.date = date; + } + if (type) { + params.type = type; + } + return http.get('/reports/search', { + params + }); +}; + +const getReportsByRouteIdAndType = (route_id, type) => { + const params = {}; + if (route_id) { + params.route_id = route_id; + } + if (type) { + params.type = type; + } + return http.get('/reports/search-route', { + params + }); +} + +const createReport = (data) => { + return http.post('/reports', data); +}; + +const updateReport = (id, data) => { + return http.put(`/reports/${id}`, data); +} + + + +export const ReportService = { + getReportsByDateAndType, + createReport, + updateReport, + getReportsByRouteIdAndType +}; diff --git a/client/src/services/ResourceService.js b/client/src/services/ResourceService.js new file mode 100644 index 0000000..a1cbf1f --- /dev/null +++ b/client/src/services/ResourceService.js @@ -0,0 +1,113 @@ +import http from "../http-common"; +const getAll = (type) => { + const params = {}; + if (type) { + params.type = type; + } + return http.get("/resources", {params}); +}; + +const createNewResource = (data) => { + data.status = 'active'; + return http.post('/resources', data); +}; + + +const updateResource = (id, data) => { + return http.put(`/resources/${id}`, data); +} + +const disableResource = (id, data) => { + return http.put(`/resources/${id}/disable`, data); +} + +const getResource = (id) => { + return http.get(`/resources/${id}`); +} + +const deleteResource = (id) => { + return http.delete(`/resources/${id}`) +} + +const resourceOptionList = [ + 'Family Medicine (PCP)', + 'Acupuncture', + 'Allergy & Asthma', + 'Audiology', + 'Behavior Health/Social Worker', + 'Botox Therapy', + 'Breast Surgery', + 'Cardiology', + 'Cardiovascular ', + 'Colon & Rectal Surgery', + 'Dentist', + 'Dermatology', + 'Dialysis', + 'Endocrinology & Diabetes', + 'Endodontist', + 'Endoscopy Center', + 'Otolaryngology (ENT)', + 'Eye Surgery Center', + 'Gastroenterology', + 'General Surgery', + 'GYN Oncology', + 'Head & Neck Surgery', + 'Health Boutique', + 'Hearing Aids', + 'Hematology & Oncology', + 'Hepatology', + 'Hospital', + 'Infectious disease', + 'Medical Center', + 'Lab', + 'Modified Barium Swallow (MBS) Study ', + 'Medical Supply', + 'Nephrology', + 'Neuro Surgeon', + 'Neurology', + 'OB/GYN', + 'Optometry (OD)', + 'Oncology', + 'Oncology Center', + 'Ophthalmology', + 'Ophthalmology (Retina Specialist)', + 'Oral Surgery', + 'Orthopaedic', + 'Osteopath', + 'Pain Management', + 'Periodontist', + 'Pharmacy', + 'Physical Therapy', + 'Physical, Occupational, & Speech Therapy', + 'Podiatry', + 'Psychiatry', + 'Pulmonology', + 'Radiation Oncology', + 'Radiology', + 'Rehabilitation', + 'Rheumatology', + 'Sleep Medicine', + 'Substance Abuse Treatment', + 'Sports Medicine', + 'Surgery', + 'Surgery Center', + 'Thoracic Surgery', + 'Traditional Chinese Medicine', + 'Urgent Care', + 'Urogynecology', + 'Urology', + 'Vascular and Vein', + 'Vascular & Interventional Radiologist', + 'Weight Loss / GYM', + 'Wound Clinic' +]; + +export const ResourceService = { + getAll, + createNewResource, + updateResource, + disableResource, + getResource, + deleteResource, + resourceOptionList +}; diff --git a/client/src/services/RouteTemplateService.js b/client/src/services/RouteTemplateService.js new file mode 100644 index 0000000..790a31e --- /dev/null +++ b/client/src/services/RouteTemplateService.js @@ -0,0 +1,23 @@ +import http from "../http-common"; +const getAll = () => { + return http.get('/route-templates'); +}; + +const updateRouteTemplate = (id, data) => { + return http.put(`/route-templates/${id}`, data); +}; + +const createNewRouteTemplate = (data) => { + return http.post('/route-templates', data); +}; + +const deleteRouteTemplate = (id) => { + return http.delete(`/route-templates/${id}`); +} + +export const RouteTemplateService = { + getAll, + updateRouteTemplate, + createNewRouteTemplate, + deleteRouteTemplate +}; diff --git a/client/src/services/SignatureRequestService.js b/client/src/services/SignatureRequestService.js new file mode 100644 index 0000000..87b696b --- /dev/null +++ b/client/src/services/SignatureRequestService.js @@ -0,0 +1,31 @@ +import http from "../http-common"; + +const getAllSignatureRequests = (params) => { + return http.get('/signature-requests', { params}); +}; + +const createNewSignatureRequest = (data) => { + data.status = 'active'; + return http.post('/signature-requests', data); +}; + +const updateSignatureRequest = (id, data) => { + return http.put(`/signature-requests/${id}`, data); +} + +const deleteSignatureRequest = (id) => { + return http.delete(`/signature-requests/${id}`) +} + +const getSignatureRequestById = (id) => { + return http.get(`/signature-requests/${id}`); +} + + +export const SignatureRequestService = { + getAllSignatureRequests, + createNewSignatureRequest, + updateSignatureRequest, + deleteSignatureRequest, + getSignatureRequestById +}; \ No newline at end of file diff --git a/client/src/services/TransRoutesService.js b/client/src/services/TransRoutesService.js new file mode 100644 index 0000000..04e8315 --- /dev/null +++ b/client/src/services/TransRoutesService.js @@ -0,0 +1,137 @@ +import http from "../http-common"; +import { PERSONAL_ROUTE_STATUS } from "../shared/constants"; +const getAll = (scheduleDate, driverId) => { + const params = {} + if (driverId) { + params.driverId = driverId; + } + if (scheduleDate) { + params.scheduleDate = scheduleDate + } + return http.get('/routes', { + params + }); +}; + +const updateRoute = (id, data) => { + return http.put(`/routes/${id}`, data); +}; + +const getRoute = (id) => { + return http.get(`/routes/${id}`); +} + +const createNewRoute = (data) => { + return http.post('/routes', data); +}; + +const deleteRoute = (id) => { + return http.delete(`/routes/${id}`); +} + +const getAllBreakfastRecords = (date) => { + const params = { date } + return http.get('/breakfasts', { + params + }) +} + +const createBreakfastRecords = (data) => { + return http.post('/breakfasts', data); +} + +const deleteBreakfastRecords = (id) => { + return http.delete(`/breakfasts/${id}`); +} + +const getAllLunchRecords = (date) => { + const params = { date } + return http.get('/lunches', { + params + }) +} + +const updateInProgress = (data) => { + return http.post('/routes/update-in-progress', data); +} + +const createLunchRecords = (data) => { + return http.post('/lunches', data); +} + +const deleteLunchRecords = (id) => { + return http.delete(`/lunches/${id}`); +} + +const getAllSnackRecords = (date) => { + const params = { date } + return http.get('/snacks', { + params + }) +} + +const createSnackRecords = (data) => { + return http.post('/snacks', data); +} + +const deleteSnackRecords = (id) => { + return http.delete(`/snacks/${id}`); +} + +const getAllInCenterCustomersFromRoutes = (routes, breakfasts) => { + let result = []; + for (const route of [...routes]) { + const customerList = route.route_customer_list.filter((i) => (i.customer_route_status === PERSONAL_ROUTE_STATUS.IN_CENTER) && !result.find(r => r.customer_id === i.customer_id))?.map(item => Object.assign({}, item, { has_breakfast: !!(breakfasts.find(b => b.customer_id === item.customer_id && b.has_breakfast)) })); + result = result.concat(customerList); + } + return result; +} + +const getAllInCenterCustomersFromRoutesForLunch = (routes, lunches) => { + let result = []; + for (const route of [...routes]) { + const customerList = route.route_customer_list.filter((i) => (i.customer_route_status === PERSONAL_ROUTE_STATUS.IN_CENTER) && !result.find(r => r.customer_id === i.customer_id))?.map(item => Object.assign({}, item, { has_lunch: !!(lunches.find(b => b.customer_id === item.customer_id && b.has_lunch)) })); + result = result.concat(customerList); + } + return result; +} + +const getAllInCenterCustomersFromRoutesForSnack = (routes, snacks) => { + let result = []; + for (const route of [...routes]) { + const customerList = route.route_customer_list.filter((i) => (i.customer_route_status === PERSONAL_ROUTE_STATUS.IN_CENTER) && !result.find(r => r.customer_id === i.customer_id))?.map(item => Object.assign({}, item, { has_snack: !!(snacks.find(b => b.customer_id === item.customer_id && b.has_snack)) })); + result = result.concat(customerList); + } + return result; +} + +const getAllCustomersFromRoutes = (routes, vehicles) => { + let result = []; + for (const route of [...routes]) { + const customerList = route.route_customer_list.filter((i) => !result.find(r => r.customer_id === i.customer_id))?.map(item => Object.assign({}, item, { vehicle_number: vehicles.find(vehicle => vehicle.id === route.vehicle)?.vehicle_number })); + result = result.concat(customerList); + } + return result; +} + +export const TransRoutesService = { + getAll, + updateRoute, + createNewRoute, + deleteRoute, + getAllBreakfastRecords, + createBreakfastRecords, + deleteBreakfastRecords, + getAllInCenterCustomersFromRoutes, + getAllCustomersFromRoutes, + getAllLunchRecords, + createLunchRecords, + deleteLunchRecords, + getAllInCenterCustomersFromRoutesForLunch, + getAllInCenterCustomersFromRoutesForSnack, + getAllSnackRecords, + createSnackRecords, + deleteSnackRecords, + updateInProgress, + getRoute +}; diff --git a/client/src/services/UserService.js b/client/src/services/UserService.js new file mode 100644 index 0000000..83eac13 --- /dev/null +++ b/client/src/services/UserService.js @@ -0,0 +1,8 @@ +import http from "../http-common"; +const getAll = () => { + return http.get("/users"); +}; + +export const UserService = { + getAll +}; diff --git a/client/src/services/VehicleService.js b/client/src/services/VehicleService.js new file mode 100644 index 0000000..b5b0d09 --- /dev/null +++ b/client/src/services/VehicleService.js @@ -0,0 +1,28 @@ +import http from "../http-common"; +const getAll = () => { + return http.get("/vehicles"); +}; +const getAllActiveVehicles = () => { + return http.get("/vehicles/active") +}; +const updateVehicles = (id, data) => { + return http.put(`/vehicles/${id}`, data) +}; +const createNewVehicles = (data) => { + return http.post('/vehicles', data); +}; +const deleteVehicles = (id, data) => { + return http.delete(`/vehicles/${id}`); +}; +const getVehicle = (id) => { + return http.get(`/vehicles/${id}`); +}; + +export const VehicleService = { + getAll, + getAllActiveVehicles, + updateVehicles, + createNewVehicles, + deleteVehicles, + getVehicle +}; diff --git a/client/src/services/index.js b/client/src/services/index.js new file mode 100644 index 0000000..d32f2df --- /dev/null +++ b/client/src/services/index.js @@ -0,0 +1,16 @@ +export * from './UserService'; +export * from './TransRoutesService'; +export * from './DriverService'; +export * from './VehicleService'; +export * from './EmployeeService'; +export * from './CustomerService'; +export * from './AuthService'; +export * from './RouteTemplateService'; +export * from './ReportService'; +export * from './DispatcherService'; +export * from './MessageService'; +export * from './CenterPhoneService'; +export * from './ResourceService'; +export * from './EventsService'; +export * from './EventRequestService'; +export * from './SignatureRequestService'; \ No newline at end of file diff --git a/client/src/services/setupInterceptor.js b/client/src/services/setupInterceptor.js new file mode 100644 index 0000000..e433560 --- /dev/null +++ b/client/src/services/setupInterceptor.js @@ -0,0 +1,29 @@ +import axiosInstance from "../http-common"; +import {AuthService} from "./AuthService"; +const setup = () => { + axiosInstance.interceptors.request.use( + (config) => { + const token = AuthService.getLocalAccessToken(); + if (token) { + // config.headers["Authorization"] = 'Bearer ' + token; // for Spring Boot back-end + config.headers["x-access-token"] = token; // for Node.js Express back-end + } + return config; + }, + (error) => { + return Promise.reject(error); + } + ); + axiosInstance.interceptors.response.use((res) => { + return Promise.resolve(res); + }, (error) => { + console.log(error); + if (error!= null && [401, 403].includes(error.response?.status)) { + AuthService.logout(); + window.location.href='/login'; + } + return Promise.reject(error); + }) + +}; +export default setup; \ No newline at end of file diff --git a/client/src/setupTests.js b/client/src/setupTests.js new file mode 100644 index 0000000..8f2609b --- /dev/null +++ b/client/src/setupTests.js @@ -0,0 +1,5 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom'; diff --git a/client/src/shared/constants/customer.constant.js b/client/src/shared/constants/customer.constant.js new file mode 100644 index 0000000..0a23023 --- /dev/null +++ b/client/src/shared/constants/customer.constant.js @@ -0,0 +1,33 @@ +export const PICKUP_STATUS = { + BOTH: 'both', + PICKUP_ONLY: 'pickupOnly', + DROPOFF_ONLY: 'dropoffOnly', + SCHEDULE_ABSENT: 'scheduleAbsent' +} + +export const PICKUP_STATUS_TEXT = { + both: 'Both', + pickupOnly: 'Pickup Only', + dropoffOnly: 'Dropoff Only', + scheduleAbsent: 'Schedule Absent' +} + +export const CUSTOMER_TYPE = { + MEMBER: 'member', + VOLUNTEER: 'volunteer', + SELF_PAY: 'selfPayMember', + VISITOR: 'visitor', + TRANSFERRED: 'transferred', + DISCHARED: 'discharged', + DECEASED: 'deceased' +} + +export const CUSTOMER_TYPE_TEXT = { + member: 'Member', + volunteer: 'Volunteer', + selfPayMember: 'Self-Pay Member', + visitor: 'Visitor', + transferred: 'Transferred', + discharged: 'Discharged', + deceased: 'Deceased' +} \ No newline at end of file diff --git a/client/src/shared/constants/employee.constant.js b/client/src/shared/constants/employee.constant.js new file mode 100644 index 0000000..d3f5ab2 --- /dev/null +++ b/client/src/shared/constants/employee.constant.js @@ -0,0 +1,133 @@ +export const EMPLOYEE_TITLE = { + // CEO: 'CEO', + // HR: 'HR', + // OPRATIONS_DIRECTOR: 'Operations Manager', + // OPERATIONS_STAFF: 'Operations Staff', + // EVENTS_DIRECTOR: 'Events Director', + // EVENTS_STAFF: 'Events Staff', + // TRANSPORTATION_DIRECTOR: 'Transportation Director', + // TRANSPORTATION_DISPATCHER: 'Transportation Dispatcher', + // DRIVER: 'Driver', + // LOBBY_MANAGER: 'Lobby Manager', + // LOBBY_SERVER: 'Lobby Server', + // CHEF: 'CHEF', + // KITCHEN_ASSISTANT: 'Kitchen Assistant', + // SOCIAL_WORKER_MANAGER: 'Social Worker Manager', + // SOCIAL_WORKER: 'Social Worker', + // HEAD_NURSE: 'Head Nurse', + // NURSE: 'Nurse', + // NURSE_ASSISTANT: 'Nurse Assistant', + // CLINICAL_FRONT_DESK: 'Clinical Front Desk' + OPRATIONS_DIRECTOR: 'Center Director', + OPERATIONS_STAFF: 'Admin Staff', + EVENTS_DIRECTOR: 'Activity Manager', + EVENTS_STAFF: 'Activity Assistant', + TRANSPORTATION_DIRECTOR: 'Transportation Manager', + TRANSPORTATION_DISPATCHER: 'Transportation Coordinator', + DRIVER: 'Driver', + LOBBY_MANAGER: 'Lobby Manager', + LOBBY_SERVER: 'Lobby Assistant', + CAREGIVER: 'Caregiver', + CHEF: 'Chef', + KITCHEN_ASSISTANT: 'Kitchen Staff', + SOCIAL_WORKER_MANAGER: 'Social Worker Manager', + SOCIAL_WORKER: 'Social Worker Assistant', + HEAD_NURSE: 'Nursing Manager', + NURSE: 'RN', + NURSE_ASSISTANT: 'CNA', + CLINICAL_FRONT_DESK: 'Medical Appointment Scheduler' +}; + +export const EMPLOYEE_TITLE_CN = { + // CEO: '总经理', + // HR: '人力资源', + // OPRATIONS_DIRECTOR: '行政主管', + // OPERATIONS_STAFF: '行政人员', + // EVENTS_DIRECTOR: '活动主管', + // EVENTS_STAFF: '活动人员', + // TRANSPORTATION_DIRECTOR: '交通主管', + // TRANSPORTATION_DISPATCHER: '交通协调', + // DRIVER: '司机', + // LOBBY_MANAGER: '大堂主管', + // LOBBY_SERVER: '大唐服务', + // CHEF: '主厨', + // KITCHEN_ASSISTANT: '厨房协助', + // SOCIAL_WORKER_MANAGER: '社工主管', + // SOCIAL_WORKER: '社工', + // HEAD_NURSE: '护士长', + // NURSE: '护士', + // NURSE_ASSISTANT: '护士助理', + // CLINICAL_FRONT_DESK: '约诊' + OPRATIONS_DIRECTOR: '中心经理', + OPERATIONS_STAFF: '行政人员', + EVENTS_DIRECTOR: '活动部经理', + EVENTS_STAFF: '活动部助理', + TRANSPORTATION_DIRECTOR: '交通部经理', + TRANSPORTATION_DISPATCHER: '交通部协管员', + DRIVER: '司机', + LOBBY_MANAGER: '大堂经理', + LOBBY_SERVER: '大堂助理', + CAREGIVER: '护理员', + CHEF: '厨师', + KITCHEN_ASSISTANT: '厨房员工', + SOCIAL_WORKER_MANAGER: '社工主管', + SOCIAL_WORKER: '社工助理', + HEAD_NURSE: '护理部主管', + NURSE: '注册护士', + NURSE_ASSISTANT: '注册护士助理', + CLINICAL_FRONT_DESK: '约诊员工' +}; + +export const EMPLOYEE_ROLES = { + ADMIN: 'admin', + DRIVER: 'driver', + EMPLOYEE_VIEWER: 'employeeViewer', + EMPLOYEE_EDITOR: 'employeeEditor', + CUSTOMER_VIEWER: 'customerViewer', + CUSTOMER_EDITOR: 'customerEditor', + MEDICAL_RESERVATION_INFO_VIEWER: 'medicalReservationInfoViewer', + MEDICAL_RESERVATION_INFO_EDITOR: 'medicalReservationInfoEditor', + MEDICAL_RESERVATION_SCHEDULE_VIEWER: 'medicalReservationScheduleViewer', + MEDICAL_RESERVATION_SCHEDULE_EDITOR: 'medicalReservationScheduleEditor', + ATTENDANCE_VIEWER: 'attendanceViewer', + ATTENDANCE_EDITOR: 'attendanceEditor', + DRIVER_VIEWER: 'driverViewer', + DRIVER_EDITOR: 'driverEditor', + VEHICLE_VIEWER: 'vehicleViewer', + VEHICLE_EDITOR: 'vehicleEditor', + ROUTE_VIEWER: 'routeViewer', + ROUTE_EDITOR: 'routeEditor', + RESOURCE_LIST_VIEWER: 'resourceListViewer', + RESOURCE_LIST_EDITOR: 'resourceListEditor' +}; + +export const EMPLOYEE_TITLE_ROLES_MAP = { + CEO: [EMPLOYEE_ROLES.ADMIN], + HR: [EMPLOYEE_ROLES.ADMIN], + OPRATIONS_DIRECTOR: [EMPLOYEE_ROLES.EMPLOYEE_EDITOR, EMPLOYEE_ROLES.EMPLOYEE_VIEWER, EMPLOYEE_ROLES.CUSTOMER_EDITOR, EMPLOYEE_ROLES.CUSTOMER_VIEWER, EMPLOYEE_ROLES.ATTENDANCE_VIEWER, EMPLOYEE_ROLES.DRIVER_EDITOR, EMPLOYEE_ROLES.DRIVER_VIEWER, EMPLOYEE_ROLES.VEHICLE_EDITOR, EMPLOYEE_ROLES.VEHICLE_VIEWER], + OPERATIONS_STAFF: [EMPLOYEE_ROLES.EMPLOYEE_VIEWER, EMPLOYEE_ROLES.CUSTOMER_VIEWER, EMPLOYEE_ROLES.ATTENDANCE_VIEWER], + EVENTS_DIRECTOR: [EMPLOYEE_ROLES.EMPLOYEE_VIEWER, EMPLOYEE_ROLES.CUSTOMER_EDITOR, EMPLOYEE_ROLES.CUSTOMER_VIEWER, EMPLOYEE_ROLES.ATTENDANCE_VIEWER], + EVENTS_STAFF: [EMPLOYEE_ROLES.CUSTOMER_EDITOR, EMPLOYEE_ROLES.CUSTOMER_VIEWER, EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_VIEWER, EMPLOYEE_ROLES.ATTENDANCE_VIEWER], + TRANSPORTATION_DIRECTOR: Object.values(EMPLOYEE_ROLES).filter((val) => val !== EMPLOYEE_ROLES.MEDICAL_RESERVATION_INFO_EDITOR && val !== EMPLOYEE_ROLES.ADMIN), + TRANSPORTATION_DISPATCHER: [EMPLOYEE_ROLES.ROUTE_VIEWER, EMPLOYEE_ROLES.ROUTE_EDITOR,EMPLOYEE_ROLES.CUSTOMER_EDITOR, EMPLOYEE_ROLES.CUSTOMER_VIEWER, EMPLOYEE_ROLES.MEDICAL_RESERVATION_INFO_VIEWER, EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_EDITOR, EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_VIEWER, EMPLOYEE_ROLES.ATTENDANCE_EDITOR, EMPLOYEE_ROLES.ATTENDANCE_VIEWER, EMPLOYEE_ROLES.DRIVER_VIEWER, EMPLOYEE_ROLES.VEHICLE_EDITOR, EMPLOYEE_ROLES.VEHICLE_VIEWER, EMPLOYEE_ROLES.RESOURCE_LIST_VIEWER, EMPLOYEE_ROLES.DRIVER], + DRIVER: [EMPLOYEE_ROLES.DRIVER], + LOBBY_MANAGER: [EMPLOYEE_ROLES.CUSTOMER_EDITOR, EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_VIEWER, EMPLOYEE_ROLES.ATTENDANCE_VIEWER, EMPLOYEE_ROLES.ATTENDANCE_EDITOR], + LOBBY_SERVER: [EMPLOYEE_ROLES.CUSTOMER_VIEWER, EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_VIEWER], + CHEF: [EMPLOYEE_ROLES.CUSTOMER_VIEWER, EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_VIEWER], + KITCHEN_ASSISTANT: [], + SOCIAL_WORKER_MANAGER: [EMPLOYEE_ROLES.EMPLOYEE_VIEWER, EMPLOYEE_ROLES.CUSTOMER_EDITOR, EMPLOYEE_ROLES.CUSTOMER_VIEWER, EMPLOYEE_ROLES.MEDICAL_RESERVATION_INFO_VIEWER, EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_VIEWER, EMPLOYEE_ROLES.ATTENDANCE_VIEWER, EMPLOYEE_ROLES.DRIVER], + SOCIAL_WORKER: [EMPLOYEE_ROLES.EMPLOYEE_VIEWER, EMPLOYEE_ROLES.CUSTOMER_VIEWER, EMPLOYEE_ROLES.MEDICAL_RESERVATION_INFO_VIEWER, EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_VIEWER, EMPLOYEE_ROLES.ATTENDANCE_VIEWER], + HEAD_NURSE: [EMPLOYEE_ROLES.CUSTOMER_VIEWER, EMPLOYEE_ROLES.CUSTOMER_EDITOR, EMPLOYEE_ROLES.MEDICAL_RESERVATION_INFO_EDITOR, EMPLOYEE_ROLES.MEDICAL_RESERVATION_INFO_VIEWER, EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_EDITOR, EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_VIEWER, EMPLOYEE_ROLES.ATTENDANCE_VIEWER, EMPLOYEE_ROLES.RESOURCE_LIST_EDITOR, EMPLOYEE_ROLES.RESOURCE_LIST_VIEWER], + NURSE: [EMPLOYEE_ROLES.CUSTOMER_VIEWER, EMPLOYEE_ROLES.CUSTOMER_EDITOR, EMPLOYEE_ROLES.MEDICAL_RESERVATION_INFO_EDITOR, EMPLOYEE_ROLES.MEDICAL_RESERVATION_INFO_VIEWER, EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_EDITOR, EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_VIEWER, EMPLOYEE_ROLES.ATTENDANCE_VIEWER, EMPLOYEE_ROLES.RESOURCE_LIST_VIEWER], + NURSE_ASSISTANT: [EMPLOYEE_ROLES.CUSTOMER_VIEWER, EMPLOYEE_ROLES.MEDICAL_RESERVATION_INFO_VIEWER, EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_VIEWER, EMPLOYEE_ROLES.ATTENDANCE_VIEWER, EMPLOYEE_ROLES.RESOURCE_LIST_VIEWER, EMPLOYEE_ROLES.ROUTE_VIEWER], + CLINICAL_FRONT_DESK: [EMPLOYEE_ROLES.CUSTOMER_VIEWER, EMPLOYEE_ROLES.CUSTOMER_EDITOR, EMPLOYEE_ROLES.MEDICAL_RESERVATION_INFO_EDITOR, EMPLOYEE_ROLES.MEDICAL_RESERVATION_INFO_VIEWER, EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_EDITOR, EMPLOYEE_ROLES.MEDICAL_RESERVATION_SCHEDULE_VIEWER, EMPLOYEE_ROLES.ATTENDANCE_VIEWER, EMPLOYEE_ROLES.RESOURCE_LIST_EDITOR, EMPLOYEE_ROLES.RESOURCE_LIST_VIEWER, EMPLOYEE_ROLES.ROUTE_VIEWER], +}; + +export const INVITATION_CODE = ['ws5801care', 'world911street']; + +// // Test Site 01 +// export const LEGACY_LINK = (window.location.hostname.includes('worldshine2.mayo.llc') || window.location.hostname.includes('site2')) ? 'http://worldshineretro2.mayo.llc/staff/login?user=bxia' : ((window.location.hostname.includes('worldshine3.mayo.llc') || window.location.hostname.includes('site3')) ? 'http://worldshineretro3.mayo.llc/staff/login?user=bxia': 'http://worldshineretro.mayo.llc/staff/login?user=leapon'); +// export const LEGACY_LINK = (window.location.hostname.includes('ws2') || window.location.hostname.includes('site2')) ? 'http://wsretro2.mayosolution.com/staff/login?user=bxia' : ((window.location.hostname.includes('ws3') || window.location.hostname.includes('site3')) ? 'http://wsretro3.mayosolution.com/staff/login?user=bxia': 'http://wsretro1.mayosolution.com/staff/login?user=leapon'); + +// Test Site 02 +// export const LEGACY_LINK = 'http://worldshineretro2.mayo.llc/staff/login?user=bxia'; \ No newline at end of file diff --git a/client/src/shared/constants/index.js b/client/src/shared/constants/index.js new file mode 100644 index 0000000..bde7fee --- /dev/null +++ b/client/src/shared/constants/index.js @@ -0,0 +1,4 @@ +export * from "./customer.constant"; +export * from "./route-status.constant"; +export * from "./employee.constant"; +export * from "./report.constant"; \ No newline at end of file diff --git a/client/src/shared/constants/report.constant.js b/client/src/shared/constants/report.constant.js new file mode 100644 index 0000000..43e6a1f --- /dev/null +++ b/client/src/shared/constants/report.constant.js @@ -0,0 +1,5 @@ +export const REPORT_TYPE = { + ADMIN_CUSTOMER_REPORT: 'admin_customer_report', + SENIOR_CONSOLIDATE_REPORT: 'senior_consolidate_report', + ROUTE_INFO_REPORT: 'route_info_report' +} \ No newline at end of file diff --git a/client/src/shared/constants/route-status.constant.js b/client/src/shared/constants/route-status.constant.js new file mode 100644 index 0000000..759aa6c --- /dev/null +++ b/client/src/shared/constants/route-status.constant.js @@ -0,0 +1,66 @@ +export const ROUTE_STATUS = { + NOT_STARTED: 'notStarted', + ENROUTE: 'enroute', + ENROUTE_TO_CENTER: 'enrouteToCenter', + SIGN_OFF: 'signedOff', + DROPPED_OFF_ALL: 'droppedOffAll', + UNEXPECTED_ABSENT: 'unexpectedAbsent', +}; + +export const PERSONAL_ROUTE_STATUS = { + DISABLED: 'disabled', + PICKED: 'picked', + IN_CENTER: 'inCenter', + LEFT_CENTER: 'leftCenter', + DROPPED_OFF: 'droppedOff', + UNEXPECTED_ABSENT: 'unexpectedAbsent', + SCHEDULED_ABSENT: 'scheduledAbsent', + NO_STATUS: 'noStatus', + SKIP_DROPOFF: 'skipDropOff', + CALLING: 'calling' +}; + +export const PERSONAL_ROUTE_STATUS_TEXT = { + 'disabled': { + text: 'Disabled', + className: 'dark-gray' + }, + 'picked': { + text: 'Picked', + className: 'light-green' + }, + 'inCenter': { + text: 'In Center', + className: 'dark-green' + }, + 'leftCenter': { + text: 'Left Center', + className: 'dark-blue' + }, + 'droppedOff': { + text: 'Dropped Off', + className: 'black' + }, + 'unexpectedAbsent': { + text: 'Unexpected Absent', + className: 'red' + }, + 'noStatus': { + text: 'No Status', + className: 'white' + }, + 'scheduledAbsent': { + text: 'Scheduled Absent', + className: 'dark-red' + }, + 'skipDropOff': { + text: 'Skip Drop Off', + className: 'light-red' + }, + 'calling': { + text: "Calling", + className: 'yellow' + } +} + +export const reportRootUrl = (window.location.hostname.includes('worldshine2.mayo.llc') || window.location.hostname.includes('site2') || window.location.hostname.includes('localhost')) ? 'https://wsutil2.mayo.llc/reports.php' : (window.location.hostname.includes('site3') || window.location.hostname.includes('worldshine3.mayo.llc')) ? 'https://wsutil3.mayo.llc/reports.php' : 'https://wsutil.mayo.llc/reports.php'; \ No newline at end of file diff --git a/client/src/shared/index.js b/client/src/shared/index.js new file mode 100644 index 0000000..d23448b --- /dev/null +++ b/client/src/shared/index.js @@ -0,0 +1 @@ +export * from "./constants"; \ No newline at end of file diff --git a/client/src/store/.DS_Store b/client/src/store/.DS_Store new file mode 100644 index 0000000..bd57652 Binary files /dev/null and b/client/src/store/.DS_Store differ diff --git a/client/src/store/customers/customers.slice.js b/client/src/store/customers/customers.slice.js new file mode 100644 index 0000000..da26ec6 --- /dev/null +++ b/client/src/store/customers/customers.slice.js @@ -0,0 +1,43 @@ +import { createSlice, CaseReducer } from "@reduxjs/toolkit"; + +const initialState = { + customers: [], + loading: false, + error: null +}; + +const createCustomer = (state, action) => {} +const createCustomerFailure = (state, action) => { + state.error = action.payload; +} + +const fetchCustomers = (state, action) => {} +const fetchCustomersFailure = (state, action) => { + state.error = action.payload; +} + +const updateCustomer = (state, action) => {}; +const updateCustomerFailure = (state, action) => { + state.error = action.payload; +}; + +const deleteCustomer = (state, action) => {}; +const deleteCustomerFailure = (state, action) => { + state.error = action.payload; +}; + +export const customerSlice = createSlice({ + name: "customer", + initialState, + reducers: { + createCustomer, + createCustomerFailure, + updateCustomer, + updateCustomerFailure, + deleteCustomer, + deleteCustomerFailure, + fetchCustomers, + fetchCustomersFailure + } +}); + diff --git a/client/src/store/customers/index.js b/client/src/store/customers/index.js new file mode 100644 index 0000000..bcdc2a4 --- /dev/null +++ b/client/src/store/customers/index.js @@ -0,0 +1 @@ +export * from "./customers.slice"; \ No newline at end of file diff --git a/client/src/store/drivers/driver.selector.js b/client/src/store/drivers/driver.selector.js new file mode 100644 index 0000000..696f158 --- /dev/null +++ b/client/src/store/drivers/driver.selector.js @@ -0,0 +1,5 @@ +import {createSelector} from "@reduxjs/toolkit"; + +export const driversSelector = (store) => store.drivers; + +export const selectAllActiveDrivers = createSelector([driversSelector], (drivers) => drivers && drivers.drivers && drivers.drivers.filter((driver) => driver.status === 'active')); diff --git a/client/src/store/drivers/driver.slice.js b/client/src/store/drivers/driver.slice.js new file mode 100644 index 0000000..fc6cc58 --- /dev/null +++ b/client/src/store/drivers/driver.slice.js @@ -0,0 +1,52 @@ +import { createSlice, CaseReducer } from "@reduxjs/toolkit"; + +const initialState = { + drivers: [], + loading: false, + error: null +}; + + +const fetchAllDrivers = (state, action) => { + state.loading = true; +} +const fetchAllDriversSuccess = (state, action) => { + state.error = null; + state.loading = false; + state.drivers = action.payload; +} +const fetchAllDriversFailure = (state, action) => { + state.drivers = []; + state.error = action.payload; +} +const createDriver = (state, action) => {} +const createDriverFailure = (state, action) => { + state.error = action.payload; +} + +const deleteDriver = (state, action) => {} +const deleteDriverFailure = (state, action) => { + state.error = action.payload; +} + +const updateDriver = (state, action) => {} +const updateDriverFailure = (state, action) => { + state.error = action.payload; +} + +export const driverSlice = createSlice({ + name: "driver", + initialState, + reducers: { + fetchAllDrivers, + fetchAllDriversSuccess, + fetchAllDriversFailure, + createDriver, + createDriverFailure, + updateDriver, + updateDriverFailure, + deleteDriver, + deleteDriverFailure + } +}); + diff --git a/client/src/store/drivers/index.js b/client/src/store/drivers/index.js new file mode 100644 index 0000000..707c979 --- /dev/null +++ b/client/src/store/drivers/index.js @@ -0,0 +1,2 @@ +export * from './driver.slice'; +export * from './driver.selector'; \ No newline at end of file diff --git a/client/src/store/employees/employee.slice.js b/client/src/store/employees/employee.slice.js new file mode 100644 index 0000000..c72e40b --- /dev/null +++ b/client/src/store/employees/employee.slice.js @@ -0,0 +1,36 @@ +import { createSlice, CaseReducer } from "@reduxjs/toolkit"; + +const initialState = { + employees: [], + loading: false, + error: null +}; + +const createEmployee = (state, action) => {} +const createEmployeeFailure = (state, action) => { + state.error = action.payload; +} + +const updateEmployee = (state, action) => {}; +const updateEmployeeFailure = (state, action) => { + state.error = action.payload; +}; + +const deleteEmployee = (state, action) => {}; +const deleteEmployeeFailure = (state, action) => { + state.error = action.payload; +}; + +export const employeeSlice = createSlice({ + name: "employee", + initialState, + reducers: { + createEmployee, + createEmployeeFailure, + updateEmployee, + updateEmployeeFailure, + deleteEmployee, + deleteEmployeeFailure + } +}); + diff --git a/client/src/store/employees/index.js b/client/src/store/employees/index.js new file mode 100644 index 0000000..4b4d641 --- /dev/null +++ b/client/src/store/employees/index.js @@ -0,0 +1 @@ +export * from "./employee.slice"; \ No newline at end of file diff --git a/client/src/store/index.js b/client/src/store/index.js new file mode 100644 index 0000000..54ea7b7 --- /dev/null +++ b/client/src/store/index.js @@ -0,0 +1,7 @@ +export * from './users'; +export * from './trans-routes'; +export * from './drivers'; +export * from './vehicles'; +export * from './employees'; +export * from './trans-routes-templates'; +export * from './customers'; \ No newline at end of file diff --git a/client/src/store/store.js b/client/src/store/store.js new file mode 100644 index 0000000..544021c --- /dev/null +++ b/client/src/store/store.js @@ -0,0 +1,35 @@ +import { configureStore, combineReducers } from '@reduxjs/toolkit' +import { userSlice } from './users'; +import { transRoutesSlice } from './trans-routes'; +import { driverSlice } from './drivers'; +import { vehicleSlice } from './vehicles'; +import { employeeSlice } from './employees'; +import { customerSlice } from './customers'; +import { transRouteTemplatesSlice } from './trans-routes-templates'; +import createSagaMiddleware from 'redux-saga' +import rootSaga from './../effects'; + +const rootReducer = combineReducers({ + users: userSlice.reducer, + transRoutes: transRoutesSlice.reducer, + drivers: driverSlice.reducer, + vehicles: vehicleSlice.reducer, + employees: employeeSlice.reducer, + transRouteTemplates: transRouteTemplatesSlice.reducer, + customers: customerSlice.reducer +}) + +const sagaMiddleware = createSagaMiddleware() +const store = configureStore( + { + reducer: rootReducer, + devTools: true, + middleware: (getDefaultMiddleware) => getDefaultMiddleware({ + serializableCheck: false, + thunk: false + }).concat(sagaMiddleware) + } +) +sagaMiddleware.run(rootSaga); + +export default store; \ No newline at end of file diff --git a/client/src/store/trans-routes-templates/index.js b/client/src/store/trans-routes-templates/index.js new file mode 100644 index 0000000..c999714 --- /dev/null +++ b/client/src/store/trans-routes-templates/index.js @@ -0,0 +1,2 @@ +export * from './trans-routes-templates.slice'; +export * from './trans-routes-templates.selector'; \ No newline at end of file diff --git a/client/src/store/trans-routes-templates/trans-routes-templates.selector.js b/client/src/store/trans-routes-templates/trans-routes-templates.selector.js new file mode 100644 index 0000000..9992ed9 --- /dev/null +++ b/client/src/store/trans-routes-templates/trans-routes-templates.selector.js @@ -0,0 +1,8 @@ +import {createSelector} from "@reduxjs/toolkit"; + +export const transRouteTemplatesSelector = (store) => store.transRouteTemplates; + +export const selectAllRouteTemplates = createSelector([transRouteTemplatesSelector], (trans) => trans && trans.templates); +export const selectAllActiveRouteTemplates = createSelector([transRouteTemplatesSelector], (trans) => trans && trans.templates?.filter((routeTemplate) => routeTemplate.status === 'active')); +export const selectInboundRouteTemplates = createSelector([transRouteTemplatesSelector], (trans) => trans && trans.templates?.filter((routeTemplate) => routeTemplate.status === 'active' && routeTemplate.type==='inbound')); +export const selectOutboundRouteTemplates = createSelector([transRouteTemplatesSelector], (trans) => trans && trans.templates?.filter((routeTemplate) => routeTemplate.status === 'active' && routeTemplate.type==='outbound')); \ No newline at end of file diff --git a/client/src/store/trans-routes-templates/trans-routes-templates.slice.js b/client/src/store/trans-routes-templates/trans-routes-templates.slice.js new file mode 100644 index 0000000..111ca77 --- /dev/null +++ b/client/src/store/trans-routes-templates/trans-routes-templates.slice.js @@ -0,0 +1,46 @@ +import { createSlice, CaseReducer } from "@reduxjs/toolkit"; + +const initialState = { + templates: [], + loading: false, + error: null +}; + + +const fetchAllRouteTemplates = (state, action) => { + state.loading = true; +}; +const fetchAllRouteTemplatesSuccess = (state, action) => { + state.error = null; + state.loading = false; + state.templates = action.payload; +}; +const fetchAllRouteTemplatesFailure = (state, action) => { + state.routes = []; + state.error = action.payload; +}; + +const updateRouteTemplate = (state, action) => {}; +const updateRouteTemplateFailure = (state, action) => { + state.error = action.payload; +}; + +const createRouteTemplate = (state, action) => {console.log('test', action.payload)}; +const createRouteTemplateFailure = (state, action) => { + state.error = action.payload; +} + +export const transRouteTemplatesSlice = createSlice({ + name: "transRouteTemplates", + initialState, + reducers: { + fetchAllRouteTemplates, + fetchAllRouteTemplatesSuccess, + fetchAllRouteTemplatesFailure, + updateRouteTemplate, + updateRouteTemplateFailure, + createRouteTemplate, + createRouteTemplateFailure + } +}); + diff --git a/client/src/store/trans-routes/index.js b/client/src/store/trans-routes/index.js new file mode 100644 index 0000000..67bada5 --- /dev/null +++ b/client/src/store/trans-routes/index.js @@ -0,0 +1,2 @@ +export * from './trans-routes.slice'; +export * from './trans-routes.selector'; \ No newline at end of file diff --git a/client/src/store/trans-routes/trans-routes.selector.js b/client/src/store/trans-routes/trans-routes.selector.js new file mode 100644 index 0000000..c39b758 --- /dev/null +++ b/client/src/store/trans-routes/trans-routes.selector.js @@ -0,0 +1,16 @@ +import {createSelector} from "@reduxjs/toolkit"; + +export const transRoutesSelector = (store) => store.transRoutes; + +export const selectAllRoutes = createSelector([transRoutesSelector], (trans) => trans && trans.routes); +export const selectTomorrowAllRoutes = createSelector([transRoutesSelector], (trans) => trans && trans.tomorrowRoutes); +export const selectHistoryRoutes = createSelector([transRoutesSelector], (trans) => trans && trans.historyRoutes); +export const selectInboundRoutes = createSelector([selectAllRoutes], (routes) => routes && routes.filter((route) => route.type === "inbound")); +export const selectOutboundRoutes = createSelector([selectAllRoutes], (routes) => routes && routes.filter((route) => route.type === "outbound")); +export const selectTomorrowInboundRoutes = createSelector([selectTomorrowAllRoutes], (routes) => routes && routes.filter((route) => route.type === "inbound")); +export const selectTomorrowOutboundRoutes = createSelector([selectTomorrowAllRoutes], (routes) => routes && routes.filter((route) => route.type === "outbound")); +export const selectHistoryInboundRoutes = createSelector([selectHistoryRoutes], (routes) => routes && routes.filter((route) => route.type === "inbound")); +export const selectHistoryOutboundRoutes = createSelector([selectHistoryRoutes], (routes) => routes && routes.filter((route) => route.type === "outbound")); +export const selectAllBreakfastRecords = createSelector([transRoutesSelector], (trans) => trans && trans.breakfasts); +export const selectAllLunchRecords = createSelector([transRoutesSelector], (trans) => trans && trans.lunches); +export const selectAllSnackRecords = createSelector([transRoutesSelector], (trans) => trans && trans.snacks); \ No newline at end of file diff --git a/client/src/store/trans-routes/trans-routes.slice.js b/client/src/store/trans-routes/trans-routes.slice.js new file mode 100644 index 0000000..e642000 --- /dev/null +++ b/client/src/store/trans-routes/trans-routes.slice.js @@ -0,0 +1,142 @@ +import { createSlice, CaseReducer } from "@reduxjs/toolkit"; + +const initialState = { + routes: [], + tomorrowRoutes: [], + historyRoutes: [], + breakfasts: [], + lunches: [], + snacks: [], + loading: false, + breakfastsLoaded: false, + error: null +}; + +const fetchAllHistoryRoutes = (state, action) => { + state.loading = true; +}; + +const fetchAllHisotryRoutesSuccess = (state, action) => { + state.error = null; + state.loading = false; + state.historyRoutes = action.payload; +} + +const fetchAllHistoryRoutesFailure = (state, action) => { + state.historyRoutes = []; + state.error = action.payload; +}; + +const fetchAllLunchRecords = (state, action) => { + state.loading = true; +}; + +const fetchAllLunchRecordsSuccess = (state, action) => { + state.lunches = action.payload; + state.error = null; + state.loading = false; +}; + +const fetchAllLunchRecordsFailure = (state, action) => { + state.lunches = []; + state.error = action.payload; +} + +const fetchAllSnackRecords = (state, action) => { + state.loading = true; +}; + +const fetchAllSnackRecordsSuccess = (state, action) => { + state.snacks = action.payload; + state.error = null; + state.loading = false; +}; + +const fetchAllSnackRecordsFailure = (state, action) => { + state.snacks = []; + state.error = action.payload; +}; + +const fetchAllBreakfastRecords = (state, action) => { + state.loading = true; + state.breakfastsLoaded = false; +}; + +const fetchAllBreakfastRecordsSuccess = (state, action) => { + state.breakfasts = action.payload; + state.error = null; + state.loading = false; + state.breakfastsLoaded = true; +}; + +const fetchAllBreakfastRecordsFailure = (state, action) => { + state.breakfasts = []; + state.error = action.payload; + state.breakfastsLoaded = false; +} + +const fetchAllRoutes = (state, action) => { + state.loading = true; +}; +const fetchAllRoutesSuccess = (state, action) => { + state.error = null; + state.loading = false; + state.routes = action.payload; +}; +const fetchAllRoutesFailure = (state, action) => { + state.routes = []; + state.error = action.payload; +}; + +const fetchAllTomorrowRoutes = (state, action) => { + state.loading = true; +}; +const fetchAllTomorrowRoutesSuccess = (state, action) => { + state.error = null; + state.loading = false; + state.tomorrowRoutes = action.payload; +}; +const fetchAllTomorrowRoutesFailure = (state, action) => { + state.tomorrowRoutes = []; + state.error = action.payload; +}; + +const updateRoute = (state, action) => {}; +const updateRouteFailure = (state, action) => { + state.error = action.payload; +}; + +const createRoute = (state, action) => {}; +const createRouteFailure = (state, action) => { + state.error = action.payload; +} + +export const transRoutesSlice = createSlice({ + name: "transRoutes", + initialState, + reducers: { + fetchAllRoutes, + fetchAllRoutesSuccess, + fetchAllRoutesFailure, + updateRoute, + updateRouteFailure, + fetchAllTomorrowRoutes, + fetchAllTomorrowRoutesSuccess, + fetchAllTomorrowRoutesFailure, + createRoute, + createRouteFailure, + fetchAllHisotryRoutesSuccess, + fetchAllHistoryRoutes, + fetchAllHistoryRoutesFailure, + fetchAllBreakfastRecords, + fetchAllBreakfastRecordsFailure, + fetchAllBreakfastRecordsSuccess, + fetchAllLunchRecords, + fetchAllLunchRecordsFailure, + fetchAllLunchRecordsSuccess, + fetchAllSnackRecords, + fetchAllSnackRecordsSuccess, + fetchAllSnackRecordsFailure + } +}); + diff --git a/client/src/store/users/index.js b/client/src/store/users/index.js new file mode 100644 index 0000000..93b3bcd --- /dev/null +++ b/client/src/store/users/index.js @@ -0,0 +1 @@ +export * from './users.slice'; \ No newline at end of file diff --git a/client/src/store/users/users.slice.js b/client/src/store/users/users.slice.js new file mode 100644 index 0000000..175ba90 --- /dev/null +++ b/client/src/store/users/users.slice.js @@ -0,0 +1,33 @@ +import { createSlice, CaseReducer } from "@reduxjs/toolkit"; + +const initialState = { + users: [], + loading: false, + error: null +}; + + +const fetchAllUsers = (state, action) => { + state.loading = true; +} +const fetchAllUsersSuccess = (state, action) => { + state.error = null; + state.loading = false; + state.users = action.payload; +} +const fetchAllUsersFailure = (state, action) => { + state.users = []; + state.error = action.payload; +} + + +export const userSlice = createSlice({ + name: "user", + initialState, + reducers: { + fetchAllUsers, + fetchAllUsersSuccess, + fetchAllUsersFailure + } +}); + diff --git a/client/src/store/vehicles/index.js b/client/src/store/vehicles/index.js new file mode 100644 index 0000000..46abcb8 --- /dev/null +++ b/client/src/store/vehicles/index.js @@ -0,0 +1,2 @@ +export * from './vehicles.slice'; +export * from './vehicles.selector'; \ No newline at end of file diff --git a/client/src/store/vehicles/vehicles.selector.js b/client/src/store/vehicles/vehicles.selector.js new file mode 100644 index 0000000..621d6ec --- /dev/null +++ b/client/src/store/vehicles/vehicles.selector.js @@ -0,0 +1,7 @@ +import {createSelector} from "@reduxjs/toolkit"; + +export const vehiclesSelector = (store) => store.vehicles; + +export const selectVehicleError = createSelector([vehiclesSelector], (vehicles) => vehicles && vehicles.error); + +export const selectAllActiveVehicles = createSelector([vehiclesSelector], (vehicles) => vehicles && vehicles.vehicles && vehicles.vehicles.filter((vehicle) => vehicle.status === 'active')); diff --git a/client/src/store/vehicles/vehicles.slice.js b/client/src/store/vehicles/vehicles.slice.js new file mode 100644 index 0000000..0e119b7 --- /dev/null +++ b/client/src/store/vehicles/vehicles.slice.js @@ -0,0 +1,52 @@ +import { createSlice, CaseReducer } from "@reduxjs/toolkit"; + +const initialState = { + vehicles: [], + loading: false, + error: null +}; + + +const fetchAllVehicles = (state, action) => { + state.loading = true; +} +const fetchAllVehiclesSuccess = (state, action) => { + state.error = null; + state.loading = false; + state.vehicles = action.payload; +} +const fetchAllVehiclesFailure = (state, action) => { + state.vehicles = []; + state.error = action.payload; +} +const updateVehicle = (state, action) => {}; +const updateVehicleFailure = (state, action) => { + state.error = action.payload; +} +const createVehicle = (state, action) => {}; +const createVehicleFailure = (state, action) => { + state.error = action.payload; +} + +const deleteVehicle = (state, action) => {}; +const deleteVehicleFailure = (state, action) => { + state.error = action.payload; +} + + +export const vehicleSlice = createSlice({ + name: "vehicle", + initialState, + reducers: { + fetchAllVehicles, + fetchAllVehiclesSuccess, + fetchAllVehiclesFailure, + updateVehicle, + updateVehicleFailure, + createVehicleFailure, + createVehicle, + deleteVehicle, + deleteVehicleFailure + } +}); + diff --git a/install_nvm.sh b/install_nvm.sh new file mode 100644 index 0000000..a6d96a6 --- /dev/null +++ b/install_nvm.sh @@ -0,0 +1,425 @@ +#!/usr/bin/env bash + +{ # this ensures the entire script is downloaded # + +nvm_has() { + type "$1" > /dev/null 2>&1 +} + +nvm_default_install_dir() { + [ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm" +} + +nvm_install_dir() { + if [ -n "$NVM_DIR" ]; then + printf %s "${NVM_DIR}" + else + nvm_default_install_dir + fi +} + +nvm_latest_version() { + echo "v0.35.0" +} + +nvm_profile_is_bash_or_zsh() { + local TEST_PROFILE + TEST_PROFILE="${1-}" + case "${TEST_PROFILE-}" in + *"/.bashrc" | *"/.bash_profile" | *"/.zshrc") + return + ;; + *) + return 1 + ;; + esac +} + +# +# Outputs the location to NVM depending on: +# * The availability of $NVM_SOURCE +# * The method used ("script" or "git" in the script, defaults to "git") +# NVM_SOURCE always takes precedence unless the method is "script-nvm-exec" +# +nvm_source() { + local NVM_METHOD + NVM_METHOD="$1" + local NVM_SOURCE_URL + NVM_SOURCE_URL="$NVM_SOURCE" + if [ "_$NVM_METHOD" = "_script-nvm-exec" ]; then + NVM_SOURCE_URL="https://raw.githubusercontent.com/nvm-sh/nvm/$(nvm_latest_version)/nvm-exec" + elif [ "_$NVM_METHOD" = "_script-nvm-bash-completion" ]; then + NVM_SOURCE_URL="https://raw.githubusercontent.com/nvm-sh/nvm/$(nvm_latest_version)/bash_completion" + elif [ -z "$NVM_SOURCE_URL" ]; then + if [ "_$NVM_METHOD" = "_script" ]; then + NVM_SOURCE_URL="https://raw.githubusercontent.com/nvm-sh/nvm/$(nvm_latest_version)/nvm.sh" + elif [ "_$NVM_METHOD" = "_git" ] || [ -z "$NVM_METHOD" ]; then + NVM_SOURCE_URL="https://github.com/nvm-sh/nvm.git" + else + echo >&2 "Unexpected value \"$NVM_METHOD\" for \$NVM_METHOD" + return 1 + fi + fi + echo "$NVM_SOURCE_URL" +} + +# +# Node.js version to install +# +nvm_node_version() { + echo "$NODE_VERSION" +} + +nvm_download() { + if nvm_has "curl"; then + curl --compressed -q "$@" + elif nvm_has "wget"; then + # Emulate curl with wget + ARGS=$(echo "$*" | command sed -e 's/--progress-bar /--progress=bar /' \ + -e 's/-L //' \ + -e 's/--compressed //' \ + -e 's/-I /--server-response /' \ + -e 's/-s /-q /' \ + -e 's/-o /-O /' \ + -e 's/-C - /-c /') + # shellcheck disable=SC2086 + eval wget $ARGS + fi +} + +install_nvm_from_git() { + local INSTALL_DIR + INSTALL_DIR="$(nvm_install_dir)" + + if [ -d "$INSTALL_DIR/.git" ]; then + echo "=> nvm is already installed in $INSTALL_DIR, trying to update using git" + command printf '\r=> ' + command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" fetch origin tag "$(nvm_latest_version)" --depth=1 2> /dev/null || { + echo >&2 "Failed to update nvm, run 'git fetch' in $INSTALL_DIR yourself." + exit 1 + } + else + # Cloning to $INSTALL_DIR + echo "=> Downloading nvm from git to '$INSTALL_DIR'" + command printf '\r=> ' + mkdir -p "${INSTALL_DIR}" + if [ "$(ls -A "${INSTALL_DIR}")" ]; then + command git init "${INSTALL_DIR}" || { + echo >&2 'Failed to initialize nvm repo. Please report this!' + exit 2 + } + command git --git-dir="${INSTALL_DIR}/.git" remote add origin "$(nvm_source)" 2> /dev/null \ + || command git --git-dir="${INSTALL_DIR}/.git" remote set-url origin "$(nvm_source)" || { + echo >&2 'Failed to add remote "origin" (or set the URL). Please report this!' + exit 2 + } + command git --git-dir="${INSTALL_DIR}/.git" fetch origin tag "$(nvm_latest_version)" --depth=1 || { + echo >&2 'Failed to fetch origin with tags. Please report this!' + exit 2 + } + else + command git -c advice.detachedHead=false clone "$(nvm_source)" -b "$(nvm_latest_version)" --depth=1 "${INSTALL_DIR}" || { + echo >&2 'Failed to clone nvm repo. Please report this!' + exit 2 + } + fi + fi + command git -c advice.detachedHead=false --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" checkout -f --quiet "$(nvm_latest_version)" + if [ -n "$(command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" show-ref refs/heads/master)" ]; then + if command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" branch --quiet 2>/dev/null; then + command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" branch --quiet -D master >/dev/null 2>&1 + else + echo >&2 "Your version of git is out of date. Please update it!" + command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" branch -D master >/dev/null 2>&1 + fi + fi + + echo "=> Compressing and cleaning up git repository" + if ! command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" reflog expire --expire=now --all; then + echo >&2 "Your version of git is out of date. Please update it!" + fi + if ! command git --git-dir="$INSTALL_DIR"/.git --work-tree="$INSTALL_DIR" gc --auto --aggressive --prune=now ; then + echo >&2 "Your version of git is out of date. Please update it!" + fi + return +} + +# +# Automatically install Node.js +# +nvm_install_node() { + local NODE_VERSION_LOCAL + NODE_VERSION_LOCAL="$(nvm_node_version)" + + if [ -z "$NODE_VERSION_LOCAL" ]; then + return 0 + fi + + echo "=> Installing Node.js version $NODE_VERSION_LOCAL" + nvm install "$NODE_VERSION_LOCAL" + local CURRENT_NVM_NODE + + CURRENT_NVM_NODE="$(nvm_version current)" + if [ "$(nvm_version "$NODE_VERSION_LOCAL")" == "$CURRENT_NVM_NODE" ]; then + echo "=> Node.js version $NODE_VERSION_LOCAL has been successfully installed" + else + echo >&2 "Failed to install Node.js $NODE_VERSION_LOCAL" + fi +} + +install_nvm_as_script() { + local INSTALL_DIR + INSTALL_DIR="$(nvm_install_dir)" + local NVM_SOURCE_LOCAL + NVM_SOURCE_LOCAL="$(nvm_source script)" + local NVM_EXEC_SOURCE + NVM_EXEC_SOURCE="$(nvm_source script-nvm-exec)" + local NVM_BASH_COMPLETION_SOURCE + NVM_BASH_COMPLETION_SOURCE="$(nvm_source script-nvm-bash-completion)" + + # Downloading to $INSTALL_DIR + mkdir -p "$INSTALL_DIR" + if [ -f "$INSTALL_DIR/nvm.sh" ]; then + echo "=> nvm is already installed in $INSTALL_DIR, trying to update the script" + else + echo "=> Downloading nvm as script to '$INSTALL_DIR'" + fi + nvm_download -s "$NVM_SOURCE_LOCAL" -o "$INSTALL_DIR/nvm.sh" || { + echo >&2 "Failed to download '$NVM_SOURCE_LOCAL'" + return 1 + } & + nvm_download -s "$NVM_EXEC_SOURCE" -o "$INSTALL_DIR/nvm-exec" || { + echo >&2 "Failed to download '$NVM_EXEC_SOURCE'" + return 2 + } & + nvm_download -s "$NVM_BASH_COMPLETION_SOURCE" -o "$INSTALL_DIR/bash_completion" || { + echo >&2 "Failed to download '$NVM_BASH_COMPLETION_SOURCE'" + return 2 + } & + for job in $(jobs -p | command sort) + do + wait "$job" || return $? + done + chmod a+x "$INSTALL_DIR/nvm-exec" || { + echo >&2 "Failed to mark '$INSTALL_DIR/nvm-exec' as executable" + return 3 + } +} + +nvm_try_profile() { + if [ -z "${1-}" ] || [ ! -f "${1}" ]; then + return 1 + fi + echo "${1}" +} + +# +# Detect profile file if not specified as environment variable +# (eg: PROFILE=~/.myprofile) +# The echo'ed path is guaranteed to be an existing file +# Otherwise, an empty string is returned +# +nvm_detect_profile() { + if [ "${PROFILE-}" = '/dev/null' ]; then + # the user has specifically requested NOT to have nvm touch their profile + return + fi + + if [ -n "${PROFILE}" ] && [ -f "${PROFILE}" ]; then + echo "${PROFILE}" + return + fi + + local DETECTED_PROFILE + DETECTED_PROFILE='' + + if [ -n "${BASH_VERSION-}" ]; then + if [ -f "$HOME/.bashrc" ]; then + DETECTED_PROFILE="$HOME/.bashrc" + elif [ -f "$HOME/.bash_profile" ]; then + DETECTED_PROFILE="$HOME/.bash_profile" + fi + elif [ -n "${ZSH_VERSION-}" ]; then + DETECTED_PROFILE="$HOME/.zshrc" + fi + + if [ -z "$DETECTED_PROFILE" ]; then + for EACH_PROFILE in ".profile" ".bashrc" ".bash_profile" ".zshrc" + do + if DETECTED_PROFILE="$(nvm_try_profile "${HOME}/${EACH_PROFILE}")"; then + break + fi + done + fi + + if [ -n "$DETECTED_PROFILE" ]; then + echo "$DETECTED_PROFILE" + fi +} + +# +# Check whether the user has any globally-installed npm modules in their system +# Node, and warn them if so. +# +nvm_check_global_modules() { + command -v npm >/dev/null 2>&1 || return 0 + + local NPM_VERSION + NPM_VERSION="$(npm --version)" + NPM_VERSION="${NPM_VERSION:--1}" + [ "${NPM_VERSION%%[!-0-9]*}" -gt 0 ] || return 0 + + local NPM_GLOBAL_MODULES + NPM_GLOBAL_MODULES="$( + npm list -g --depth=0 | + command sed -e '/ npm@/d' -e '/ (empty)$/d' + )" + + local MODULE_COUNT + MODULE_COUNT="$( + command printf %s\\n "$NPM_GLOBAL_MODULES" | + command sed -ne '1!p' | # Remove the first line + wc -l | command tr -d ' ' # Count entries + )" + + if [ "${MODULE_COUNT}" != '0' ]; then + # shellcheck disable=SC2016 + echo '=> You currently have modules installed globally with `npm`. These will no' + # shellcheck disable=SC2016 + echo '=> longer be linked to the active version of Node when you install a new node' + # shellcheck disable=SC2016 + echo '=> with `nvm`; and they may (depending on how you construct your `$PATH`)' + # shellcheck disable=SC2016 + echo '=> override the binaries of modules installed with `nvm`:' + echo + + command printf %s\\n "$NPM_GLOBAL_MODULES" + echo '=> If you wish to uninstall them at a later point (or re-install them under your' + # shellcheck disable=SC2016 + echo '=> `nvm` Nodes), you can remove them from the system Node as follows:' + echo + echo ' $ nvm use system' + echo ' $ npm uninstall -g a_module' + echo + fi +} + +nvm_do_install() { + if [ -n "${NVM_DIR-}" ] && ! [ -d "${NVM_DIR}" ]; then + if [ -e "${NVM_DIR}" ]; then + echo >&2 "File \"${NVM_DIR}\" has the same name as installation directory." + exit 1 + fi + + if [ "${NVM_DIR}" = "$(nvm_default_install_dir)" ]; then + mkdir "${NVM_DIR}" + else + echo >&2 "You have \$NVM_DIR set to \"${NVM_DIR}\", but that directory does not exist. Check your profile files and environment." + exit 1 + fi + fi + if [ -z "${METHOD}" ]; then + # Autodetect install method + if nvm_has git; then + install_nvm_from_git + elif nvm_has nvm_download; then + install_nvm_as_script + else + echo >&2 'You need git, curl, or wget to install nvm' + exit 1 + fi + elif [ "${METHOD}" = 'git' ]; then + if ! nvm_has git; then + echo >&2 "You need git to install nvm" + exit 1 + fi + install_nvm_from_git + elif [ "${METHOD}" = 'script' ]; then + if ! nvm_has nvm_download; then + echo >&2 "You need curl or wget to install nvm" + exit 1 + fi + install_nvm_as_script + else + echo >&2 "The environment variable \$METHOD is set to \"${METHOD}\", which is not recognized as a valid installation method." + exit 1 + fi + + echo + + local NVM_PROFILE + NVM_PROFILE="$(nvm_detect_profile)" + local PROFILE_INSTALL_DIR + PROFILE_INSTALL_DIR="$(nvm_install_dir | command sed "s:^$HOME:\$HOME:")" + + SOURCE_STR="\\nexport NVM_DIR=\"${PROFILE_INSTALL_DIR}\"\\n[ -s \"\$NVM_DIR/nvm.sh\" ] && \\. \"\$NVM_DIR/nvm.sh\" # This loads nvm\\n" + + # shellcheck disable=SC2016 + COMPLETION_STR='[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion\n' + BASH_OR_ZSH=false + + if [ -z "${NVM_PROFILE-}" ] ; then + local TRIED_PROFILE + if [ -n "${PROFILE}" ]; then + TRIED_PROFILE="${NVM_PROFILE} (as defined in \$PROFILE), " + fi + echo "=> Profile not found. Tried ${TRIED_PROFILE-}~/.bashrc, ~/.bash_profile, ~/.zshrc, and ~/.profile." + echo "=> Create one of them and run this script again" + echo " OR" + echo "=> Append the following lines to the correct file yourself:" + command printf "${SOURCE_STR}" + echo + else + if nvm_profile_is_bash_or_zsh "${NVM_PROFILE-}"; then + BASH_OR_ZSH=true + fi + if ! command grep -qc '/nvm.sh' "$NVM_PROFILE"; then + echo "=> Appending nvm source string to $NVM_PROFILE" + command printf "${SOURCE_STR}" >> "$NVM_PROFILE" + else + echo "=> nvm source string already in ${NVM_PROFILE}" + fi + # shellcheck disable=SC2016 + if ${BASH_OR_ZSH} && ! command grep -qc '$NVM_DIR/bash_completion' "$NVM_PROFILE"; then + echo "=> Appending bash_completion source string to $NVM_PROFILE" + command printf "$COMPLETION_STR" >> "$NVM_PROFILE" + else + echo "=> bash_completion source string already in ${NVM_PROFILE}" + fi + fi + if ${BASH_OR_ZSH} && [ -z "${NVM_PROFILE-}" ] ; then + echo "=> Please also append the following lines to the if you are using bash/zsh shell:" + command printf "${COMPLETION_STR}" + fi + + # Source nvm + # shellcheck source=/dev/null + \. "$(nvm_install_dir)/nvm.sh" + + nvm_check_global_modules + + nvm_install_node + + nvm_reset + + echo "=> Close and reopen your terminal to start using nvm or run the following to use it now:" + command printf "${SOURCE_STR}" + if ${BASH_OR_ZSH} ; then + command printf "${COMPLETION_STR}" + fi +} + +# +# Unsets the various functions defined +# during the execution of the install script +# +nvm_reset() { + unset -f nvm_has nvm_install_dir nvm_latest_version nvm_profile_is_bash_or_zsh \ + nvm_source nvm_node_version nvm_download install_nvm_from_git nvm_install_node \ + install_nvm_as_script nvm_try_profile nvm_detect_profile nvm_check_global_modules \ + nvm_do_install nvm_reset nvm_default_install_dir +} + +[ "_$NVM_ENV" = "_testing" ] || nvm_do_install + +} # this ensures the entire script is downloaded # diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7e74a18 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3010 @@ +{ + "name": "worldshine", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "worldshine", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "axios": "^0.27.2", + "bcryptjs": "^2.4.3", + "body-parser": "^1.20.0", + "cors": "^2.8.5", + "docxtemplater": "^3.47.1", + "express": "^4.18.1", + "jsonwebtoken": "^8.5.1", + "libreoffice-convert": "^1.0.5", + "moment": "^2.30.1", + "moment-timezone": "^0.5.45", + "mongoose": "^6.3.2", + "mongoose-unique-validator": "^3.0.0", + "multer": "^1.4.4", + "multer-gridfs-storage": "^5.0.2", + "pizzip": "^3.1.7", + "xlsx-template": "^1.4.4" + } + }, + "node_modules/@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "optional": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/ie11-detection/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "optional": true, + "dependencies": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "optional": true, + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "optional": true, + "dependencies": { + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/supports-web-crypto/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, + "node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "optional": true + }, + "node_modules/@aws-sdk/client-cognito-identity": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.577.0.tgz", + "integrity": "sha512-y1fHORHoufrzj2GcnY52g4ykemFpT0Hu9e9kYa6yR0weQ0WalcG7WcnMNasXMcjr9fDjNze7ZCTuWJSI+HwkTQ==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sso-oidc": "3.577.0", + "@aws-sdk/client-sts": "3.577.0", + "@aws-sdk/core": "3.576.0", + "@aws-sdk/credential-provider-node": "3.577.0", + "@aws-sdk/middleware-host-header": "3.577.0", + "@aws-sdk/middleware-logger": "3.577.0", + "@aws-sdk/middleware-recursion-detection": "3.577.0", + "@aws-sdk/middleware-user-agent": "3.577.0", + "@aws-sdk/region-config-resolver": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@aws-sdk/util-endpoints": "3.577.0", + "@aws-sdk/util-user-agent-browser": "3.577.0", + "@aws-sdk/util-user-agent-node": "3.577.0", + "@smithy/config-resolver": "^3.0.0", + "@smithy/core": "^2.0.0", + "@smithy/fetch-http-handler": "^3.0.0", + "@smithy/hash-node": "^3.0.0", + "@smithy/invalid-dependency": "^3.0.0", + "@smithy/middleware-content-length": "^3.0.0", + "@smithy/middleware-endpoint": "^3.0.0", + "@smithy/middleware-retry": "^3.0.0", + "@smithy/middleware-serde": "^3.0.0", + "@smithy/middleware-stack": "^3.0.0", + "@smithy/node-config-provider": "^3.0.0", + "@smithy/node-http-handler": "^3.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/smithy-client": "^3.0.0", + "@smithy/types": "^3.0.0", + "@smithy/url-parser": "^3.0.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.0", + "@smithy/util-defaults-mode-node": "^3.0.0", + "@smithy/util-endpoints": "^2.0.0", + "@smithy/util-middleware": "^3.0.0", + "@smithy/util-retry": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.577.0.tgz", + "integrity": "sha512-BwujdXrydlk6UEyPmewm5GqG4nkQ6OVyRhS/SyZP/6UKSFv2/sf391Cmz0hN0itUTH1rR4XeLln8XCOtarkrzg==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.576.0", + "@aws-sdk/middleware-host-header": "3.577.0", + "@aws-sdk/middleware-logger": "3.577.0", + "@aws-sdk/middleware-recursion-detection": "3.577.0", + "@aws-sdk/middleware-user-agent": "3.577.0", + "@aws-sdk/region-config-resolver": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@aws-sdk/util-endpoints": "3.577.0", + "@aws-sdk/util-user-agent-browser": "3.577.0", + "@aws-sdk/util-user-agent-node": "3.577.0", + "@smithy/config-resolver": "^3.0.0", + "@smithy/core": "^2.0.0", + "@smithy/fetch-http-handler": "^3.0.0", + "@smithy/hash-node": "^3.0.0", + "@smithy/invalid-dependency": "^3.0.0", + "@smithy/middleware-content-length": "^3.0.0", + "@smithy/middleware-endpoint": "^3.0.0", + "@smithy/middleware-retry": "^3.0.0", + "@smithy/middleware-serde": "^3.0.0", + "@smithy/middleware-stack": "^3.0.0", + "@smithy/node-config-provider": "^3.0.0", + "@smithy/node-http-handler": "^3.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/smithy-client": "^3.0.0", + "@smithy/types": "^3.0.0", + "@smithy/url-parser": "^3.0.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.0", + "@smithy/util-defaults-mode-node": "^3.0.0", + "@smithy/util-endpoints": "^2.0.0", + "@smithy/util-middleware": "^3.0.0", + "@smithy/util-retry": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.577.0.tgz", + "integrity": "sha512-njmKSPDWueWWYVFpFcZ2P3fI6/pdQVDa0FgCyYZhOnJLgEHZIcBBg1AsnkVWacBuLopp9XVt2m+7hO6ugY1/1g==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.577.0", + "@aws-sdk/core": "3.576.0", + "@aws-sdk/credential-provider-node": "3.577.0", + "@aws-sdk/middleware-host-header": "3.577.0", + "@aws-sdk/middleware-logger": "3.577.0", + "@aws-sdk/middleware-recursion-detection": "3.577.0", + "@aws-sdk/middleware-user-agent": "3.577.0", + "@aws-sdk/region-config-resolver": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@aws-sdk/util-endpoints": "3.577.0", + "@aws-sdk/util-user-agent-browser": "3.577.0", + "@aws-sdk/util-user-agent-node": "3.577.0", + "@smithy/config-resolver": "^3.0.0", + "@smithy/core": "^2.0.0", + "@smithy/fetch-http-handler": "^3.0.0", + "@smithy/hash-node": "^3.0.0", + "@smithy/invalid-dependency": "^3.0.0", + "@smithy/middleware-content-length": "^3.0.0", + "@smithy/middleware-endpoint": "^3.0.0", + "@smithy/middleware-retry": "^3.0.0", + "@smithy/middleware-serde": "^3.0.0", + "@smithy/middleware-stack": "^3.0.0", + "@smithy/node-config-provider": "^3.0.0", + "@smithy/node-http-handler": "^3.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/smithy-client": "^3.0.0", + "@smithy/types": "^3.0.0", + "@smithy/url-parser": "^3.0.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.0", + "@smithy/util-defaults-mode-node": "^3.0.0", + "@smithy/util-endpoints": "^2.0.0", + "@smithy/util-middleware": "^3.0.0", + "@smithy/util-retry": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.577.0.tgz", + "integrity": "sha512-509Kklimva1XVlhGbpTpeX3kOP6ORpm44twJxDHpa9TURbmoaxj7veWlnLCbDorxDTrbsDghvYZshvcLsojVpg==", + "optional": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sso-oidc": "3.577.0", + "@aws-sdk/core": "3.576.0", + "@aws-sdk/credential-provider-node": "3.577.0", + "@aws-sdk/middleware-host-header": "3.577.0", + "@aws-sdk/middleware-logger": "3.577.0", + "@aws-sdk/middleware-recursion-detection": "3.577.0", + "@aws-sdk/middleware-user-agent": "3.577.0", + "@aws-sdk/region-config-resolver": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@aws-sdk/util-endpoints": "3.577.0", + "@aws-sdk/util-user-agent-browser": "3.577.0", + "@aws-sdk/util-user-agent-node": "3.577.0", + "@smithy/config-resolver": "^3.0.0", + "@smithy/core": "^2.0.0", + "@smithy/fetch-http-handler": "^3.0.0", + "@smithy/hash-node": "^3.0.0", + "@smithy/invalid-dependency": "^3.0.0", + "@smithy/middleware-content-length": "^3.0.0", + "@smithy/middleware-endpoint": "^3.0.0", + "@smithy/middleware-retry": "^3.0.0", + "@smithy/middleware-serde": "^3.0.0", + "@smithy/middleware-stack": "^3.0.0", + "@smithy/node-config-provider": "^3.0.0", + "@smithy/node-http-handler": "^3.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/smithy-client": "^3.0.0", + "@smithy/types": "^3.0.0", + "@smithy/url-parser": "^3.0.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.0", + "@smithy/util-defaults-mode-node": "^3.0.0", + "@smithy/util-endpoints": "^2.0.0", + "@smithy/util-middleware": "^3.0.0", + "@smithy/util-retry": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.576.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.576.0.tgz", + "integrity": "sha512-KDvDlbeipSTIf+ffKtTg1m419TK7s9mZSWC8bvuZ9qx6/sjQFOXIKOVqyuli6DnfxGbvRcwoRuY99OcCH1N/0w==", + "optional": true, + "dependencies": { + "@smithy/core": "^2.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/signature-v4": "^3.0.0", + "@smithy/smithy-client": "^3.0.0", + "@smithy/types": "^3.0.0", + "fast-xml-parser": "4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.577.0.tgz", + "integrity": "sha512-y5yo4RKQSIQEOGLMLziLh0MZ+CxLs2QmTRjh8PkL8ovy12FPyou9Ptr7hIDD5SnCsiItJful5qbmj9e2QSmozw==", + "optional": true, + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.577.0.tgz", + "integrity": "sha512-Jxu255j0gToMGEiqufP8ZtKI8HW90lOLjwJ3LrdlD/NLsAY0tOQf1fWc53u28hWmmNGMxmCrL2p66IOgMDhDUw==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.577.0.tgz", + "integrity": "sha512-n++yhCp67b9+ZRGEdY1jhamB5E/O+QsIDOPSuRmdaSGMCOd82oUEKPgIVEU1bkqxDsBxgiEWuvtfhK6sNiDS0A==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@smithy/fetch-http-handler": "^3.0.0", + "@smithy/node-http-handler": "^3.0.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/smithy-client": "^3.0.0", + "@smithy/types": "^3.0.0", + "@smithy/util-stream": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.577.0.tgz", + "integrity": "sha512-q7lHPtv6BjRvChUE3m0tIaEZKxPTaZ1B3lKxGYsFl3VLAu5N8yGCUKwuA1izf4ucT+LyKscVGqK6VDZx1ev3nw==", + "optional": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.577.0", + "@aws-sdk/credential-provider-process": "3.577.0", + "@aws-sdk/credential-provider-sso": "3.577.0", + "@aws-sdk/credential-provider-web-identity": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@smithy/credential-provider-imds": "^3.0.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/shared-ini-file-loader": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.577.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.577.0.tgz", + "integrity": "sha512-epZ1HOMsrXBNczc0HQpv0VMjqAEpc09DUA7Rg3gUJfn8umhML7A7bXnUyqPA+S54q397UYg1leQKdSn23OiwQQ==", + "optional": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.577.0", + "@aws-sdk/credential-provider-http": "3.577.0", + "@aws-sdk/credential-provider-ini": "3.577.0", + "@aws-sdk/credential-provider-process": "3.577.0", + "@aws-sdk/credential-provider-sso": "3.577.0", + "@aws-sdk/credential-provider-web-identity": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@smithy/credential-provider-imds": "^3.0.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/shared-ini-file-loader": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.577.0.tgz", + "integrity": "sha512-Gin6BWtOiXxIgITrJ3Nwc+Y2P1uVT6huYR4EcbA/DJUPWyO0n9y5UFLewPvVbLkRn15JeEqErBLUrHclkiOKtw==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/shared-ini-file-loader": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.577.0.tgz", + "integrity": "sha512-iVm5SQvS7EgZTJsRaqUOmDQpBQPPPat42SCbWFvFQOLrl8qewq8OP94hFS5w2mP62zngeYzqhJnDel79HXbxew==", + "optional": true, + "dependencies": { + "@aws-sdk/client-sso": "3.577.0", + "@aws-sdk/token-providers": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/shared-ini-file-loader": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.577.0.tgz", + "integrity": "sha512-ZGHGNRaCtJJmszb9UTnC7izNCtRUttdPlLdMkh41KPS32vfdrBDHs1JrpbZijItRj1xKuOXsiYSXLAaHGcLh8Q==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.577.0" + } + }, + "node_modules/@aws-sdk/credential-providers": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.577.0.tgz", + "integrity": "sha512-/fzdyyAetJxTPH8f2bh1UkcN48dScLb6LjBj9+wX2BHyKSZUal7+TqPTyme4f3pj1I1EeKhDIYKldR8YyMPIAg==", + "optional": true, + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.577.0", + "@aws-sdk/client-sso": "3.577.0", + "@aws-sdk/client-sts": "3.577.0", + "@aws-sdk/credential-provider-cognito-identity": "3.577.0", + "@aws-sdk/credential-provider-env": "3.577.0", + "@aws-sdk/credential-provider-http": "3.577.0", + "@aws-sdk/credential-provider-ini": "3.577.0", + "@aws-sdk/credential-provider-node": "3.577.0", + "@aws-sdk/credential-provider-process": "3.577.0", + "@aws-sdk/credential-provider-sso": "3.577.0", + "@aws-sdk/credential-provider-web-identity": "3.577.0", + "@aws-sdk/types": "3.577.0", + "@smithy/credential-provider-imds": "^3.0.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.577.0.tgz", + "integrity": "sha512-9ca5MJz455CODIVXs0/sWmJm7t3QO4EUa1zf8pE8grLpzf0J94bz/skDWm37Pli13T3WaAQBHCTiH2gUVfCsWg==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.577.0.tgz", + "integrity": "sha512-aPFGpGjTZcJYk+24bg7jT4XdIp42mFXSuPt49lw5KygefLyJM/sB0bKKqPYYivW0rcuZ9brQ58eZUNthrzYAvg==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.577.0.tgz", + "integrity": "sha512-pn3ZVEd2iobKJlR3H+bDilHjgRnNrQ6HMmK9ZzZw89Ckn3Dcbv48xOv4RJvu0aU8SDLl/SNCxppKjeLDTPGBNA==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.577.0.tgz", + "integrity": "sha512-P55HAXgwmiHHpFx5JEPvOnAbfhN7v6sWv9PBQs+z2tC7QiBcPS0cdJR6PfV7J1n4VPK52/OnrK3l9VxdQ7Ms0g==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@aws-sdk/util-endpoints": "3.577.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.577.0.tgz", + "integrity": "sha512-4ChCFACNwzqx/xjg3zgFcW8Ali6R9C95cFECKWT/7CUM1D0MGvkclSH2cLarmHCmJgU6onKkJroFtWp0kHhgyg==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@smithy/node-config-provider": "^3.0.0", + "@smithy/types": "^3.0.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.577.0.tgz", + "integrity": "sha512-0CkIZpcC3DNQJQ1hDjm2bdSy/Xjs7Ny5YvSsacasGOkNfk+FdkiQy6N67bZX3Zbc9KIx+Nz4bu3iDeNSNplnnQ==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/shared-ini-file-loader": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.577.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.577.0.tgz", + "integrity": "sha512-FT2JZES3wBKN/alfmhlo+3ZOq/XJ0C7QOZcDNrpKjB0kqYoKjhVKZ/Hx6ArR0czkKfHzBBEs6y40ebIHx2nSmA==", + "optional": true, + "dependencies": { + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.577.0.tgz", + "integrity": "sha512-FjuUz1Kdy4Zly2q/c58tpdqHd6z7iOdU/caYzoc8jwgAHBDBbIJNQLCU9hXJnPV2M8pWxQDyIZsoVwtmvErPzw==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@smithy/types": "^3.0.0", + "@smithy/util-endpoints": "^2.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.568.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", + "integrity": "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.577.0.tgz", + "integrity": "sha512-zEAzHgR6HWpZOH7xFgeJLc6/CzMcx4nxeQolZxVZoB5pPaJd3CjyRhZN0xXeZB0XIRCWmb4yJBgyiugXLNMkLA==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@smithy/types": "^3.0.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.577.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.577.0.tgz", + "integrity": "sha512-XqvtFjbSMtycZTWVwDe8DRWovuoMbA54nhUoZwVU6rW9OSD6NZWGR512BUGHFaWzW0Wg8++Dj10FrKTG2XtqfA==", + "optional": true, + "dependencies": { + "@aws-sdk/types": "3.577.0", + "@smithy/node-config-provider": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "optional": true, + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@kant2002/jszip": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@kant2002/jszip/-/jszip-2.7.1.tgz", + "integrity": "sha512-2dpo1verOaz8KoPfOGQXHHCuBzubEV9ZXTw7cBx48XTF6y9+Owc83w9R7yx1SjAti8x5QYUEu5O+25SatFXMcA==", + "dependencies": { + "pako": "~1.0.2" + } + }, + "node_modules/@kant2002/jszip/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.7.tgz", + "integrity": "sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==", + "optional": true, + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-p6GlFGBt9K4MYLu72YuJ523NVR4A8oHlC5M2JO6OmQqN8kAc/uh1JqLE+FizTokrSJGg0CSvC+BrsmGzKtsZKA==", + "optional": true, + "dependencies": { + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.0.tgz", + "integrity": "sha512-2GzOfADwYLQugYkKQhIyZyQlM05K+tMKvRnc6eFfZcpJGRfKoMUMYdPlBKmqHwQFXQKBrGV6cxL9oymWgDzvFw==", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^3.0.0", + "@smithy/types": "^3.0.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.0.1.tgz", + "integrity": "sha512-rcMkjvwxH/bER+oZUPR0yTA0ELD6m3A+d92+CFkdF6HJFCBB1bXo7P5pm21L66XwTN01B6bUhSCQ7cymWRD8zg==", + "optional": true, + "dependencies": { + "@smithy/middleware-endpoint": "^3.0.0", + "@smithy/middleware-retry": "^3.0.1", + "@smithy/middleware-serde": "^3.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/smithy-client": "^3.0.1", + "@smithy/types": "^3.0.0", + "@smithy/util-middleware": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.0.0.tgz", + "integrity": "sha512-lfmBiFQcA3FsDAPxNfY0L7CawcWtbyWsBOHo34nF095728JLkBX4Y9q/VPPE2r7fqMVK+drmDigqE2/SSQeVRA==", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^3.0.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/types": "^3.0.0", + "@smithy/url-parser": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.0.1.tgz", + "integrity": "sha512-uaH74i5BDj+rBwoQaXioKpI0SHBJFtOVwzrCpxZxphOW0ki5jhj7dXvDMYM2IJem8TpdFvS2iC08sjOblfFGFg==", + "optional": true, + "dependencies": { + "@smithy/protocol-http": "^4.0.0", + "@smithy/querystring-builder": "^3.0.0", + "@smithy/types": "^3.0.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/hash-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.0.tgz", + "integrity": "sha512-84qXstNemP3XS5jcof0el6+bDfjzuvhJPQTEfro3lgtbCtKgzPm3MgiS6ehXVPjeQ5+JS0HqmTz8f/RYfzHVxw==", + "optional": true, + "dependencies": { + "@smithy/types": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.0.tgz", + "integrity": "sha512-F6wBBaEFgJzj0s4KUlliIGPmqXemwP6EavgvDqYwCH40O5Xr2iMHvS8todmGVZtuJCorBkXsYLyTu4PuizVq5g==", + "optional": true, + "dependencies": { + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.0.tgz", + "integrity": "sha512-3C4s4d/iGobgCtk2tnWW6+zSTOBg1PRAm2vtWZLdriwTroFbbWNSr3lcyzHdrQHnEXYCC5K52EbpfodaIUY8sg==", + "optional": true, + "dependencies": { + "@smithy/protocol-http": "^4.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.0.0.tgz", + "integrity": "sha512-aXOAWztw/5qAfp0NcA2OWpv6ZI/E+Dh9mByif7i91D/0iyYNUcKvskmXiowKESFkuZ7PIMd3VOR4fTibZDs2OQ==", + "optional": true, + "dependencies": { + "@smithy/middleware-serde": "^3.0.0", + "@smithy/node-config-provider": "^3.0.0", + "@smithy/shared-ini-file-loader": "^3.0.0", + "@smithy/types": "^3.0.0", + "@smithy/url-parser": "^3.0.0", + "@smithy/util-middleware": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.1.tgz", + "integrity": "sha512-hBhSEuL841FhJBK/19WpaGk5YWSzFk/P2UaVjANGKRv3eYNO8Y1lANWgqnuPWjOyCEWMPr58vELFDWpxvRKANw==", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^3.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/service-error-classification": "^3.0.0", + "@smithy/smithy-client": "^3.0.1", + "@smithy/types": "^3.0.0", + "@smithy/util-middleware": "^3.0.0", + "@smithy/util-retry": "^3.0.0", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.0.tgz", + "integrity": "sha512-I1vKG1foI+oPgG9r7IMY1S+xBnmAn1ISqployvqkwHoSb8VPsngHDTOgYGYBonuOKndaWRUGJZrKYYLB+Ane6w==", + "optional": true, + "dependencies": { + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.0.tgz", + "integrity": "sha512-+H0jmyfAyHRFXm6wunskuNAqtj7yfmwFB6Fp37enytp2q047/Od9xetEaUbluyImOlGnGpaVGaVfjwawSr+i6Q==", + "optional": true, + "dependencies": { + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.0.0.tgz", + "integrity": "sha512-buqfaSdDh0zo62EPLf8rGDvcpKwGpO5ho4bXS2cdFhlOta7tBkWJt+O5uiaAeICfIOfPclNOndshDNSanX2X9g==", + "optional": true, + "dependencies": { + "@smithy/property-provider": "^3.0.0", + "@smithy/shared-ini-file-loader": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.0.0.tgz", + "integrity": "sha512-3trD4r7NOMygwLbUJo4eodyQuypAWr7uvPnebNJ9a70dQhVn+US8j/lCnvoJS6BXfZeF7PkkkI0DemVJw+n+eQ==", + "optional": true, + "dependencies": { + "@smithy/abort-controller": "^3.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/querystring-builder": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.0.0.tgz", + "integrity": "sha512-LmbPgHBswdXCrkWWuUwBm9w72S2iLWyC/5jet9/Y9cGHtzqxi+GVjfCfahkvNV4KXEwgnH8EMpcrD9RUYe0eLQ==", + "optional": true, + "dependencies": { + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.0.0.tgz", + "integrity": "sha512-qOQZOEI2XLWRWBO9AgIYuHuqjZ2csyr8/IlgFDHDNuIgLAMRx2Bl8ck5U5D6Vh9DPdoaVpuzwWMa0xcdL4O/AQ==", + "optional": true, + "dependencies": { + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.0.tgz", + "integrity": "sha512-bW8Fi0NzyfkE0TmQphDXr1AmBDbK01cA4C1Z7ggwMAU5RDz5AAv/KmoRwzQAS0kxXNf/D2ALTEgwK0U2c4LtRg==", + "optional": true, + "dependencies": { + "@smithy/types": "^3.0.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.0.tgz", + "integrity": "sha512-UzHwthk0UEccV4dHzPySnBy34AWw3V9lIqUTxmozQ+wPDAO9csCWMfOLe7V9A2agNYy7xE+Pb0S6K/J23JSzfQ==", + "optional": true, + "dependencies": { + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.0.tgz", + "integrity": "sha512-3BsBtOUt2Gsnc3X23ew+r2M71WwtpHfEDGhHYHSDg6q1t8FrWh15jT25DLajFV1H+PpxAJ6gqe9yYeRUsmSdFA==", + "optional": true, + "dependencies": { + "@smithy/types": "^3.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.0.0.tgz", + "integrity": "sha512-REVw6XauXk8xE4zo5aGL7Rz4ywA8qNMUn8RtWeTRQsgAlmlvbJ7CEPBcaXU2NDC3AYBgYAXrGyWD8XrN8UGDog==", + "optional": true, + "dependencies": { + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-3.0.0.tgz", + "integrity": "sha512-kXFOkNX+BQHe2qnLxpMEaCRGap9J6tUGLzc3A9jdn+nD4JdMwCKTJ+zFwQ20GkY+mAXGatyTw3HcoUlR39HwmA==", + "optional": true, + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/types": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.0", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.0.1.tgz", + "integrity": "sha512-KAiFY4Y4jdHxR+4zerH/VBhaFKM8pbaVmJZ/CWJRwtM/CmwzTfXfvYwf6GoUwiHepdv+lwiOXCuOl6UBDUEINw==", + "optional": true, + "dependencies": { + "@smithy/middleware-endpoint": "^3.0.0", + "@smithy/middleware-stack": "^3.0.0", + "@smithy/protocol-http": "^4.0.0", + "@smithy/types": "^3.0.0", + "@smithy/util-stream": "^3.0.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.0.0.tgz", + "integrity": "sha512-VvWuQk2RKFuOr98gFhjca7fkBS+xLLURT8bUjk5XQoV0ZLm7WPwWPPY3/AwzTLuUBDeoKDCthfe1AsTUWaSEhw==", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.0.tgz", + "integrity": "sha512-2XLazFgUu+YOGHtWihB3FSLAfCUajVfNBXGGYjOaVKjLAuAxx3pSBY3hBgLzIgB17haf59gOG3imKqTy8mcrjw==", + "optional": true, + "dependencies": { + "@smithy/querystring-parser": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "optional": true, + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "optional": true, + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.1.tgz", + "integrity": "sha512-nW5kEzdJn1Bn5TF+gOPHh2rcPli8JU9vSSXLbfg7uPnfR1TMRQqs9zlYRhIb87NeSxIbpdXOI94tvXSy+fvDYg==", + "optional": true, + "dependencies": { + "@smithy/property-provider": "^3.0.0", + "@smithy/smithy-client": "^3.0.1", + "@smithy/types": "^3.0.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.1.tgz", + "integrity": "sha512-TFk+Qb+elLc/MOhtSp+50fstyfZ6avQbgH2d96xUBpeScu+Al9elxv+UFAjaTHe0HQe5n+wem8ZLpXvU8lwV6Q==", + "optional": true, + "dependencies": { + "@smithy/config-resolver": "^3.0.0", + "@smithy/credential-provider-imds": "^3.0.0", + "@smithy/node-config-provider": "^3.0.0", + "@smithy/property-provider": "^3.0.0", + "@smithy/smithy-client": "^3.0.1", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.0.0.tgz", + "integrity": "sha512-+exaXzEY3DNt2qtA2OtRNSDlVrE4p32j1JSsQkzA5AdP0YtJNjkYbYhJxkFmPYcjI1abuwopOZCwUmv682QkiQ==", + "optional": true, + "dependencies": { + "@smithy/node-config-provider": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.0.tgz", + "integrity": "sha512-q5ITdOnV2pXHSVDnKWrwgSNTDBAMHLptFE07ua/5Ty5WJ11bvr0vk2a7agu7qRhrCFRQlno5u3CneU5EELK+DQ==", + "optional": true, + "dependencies": { + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.0.tgz", + "integrity": "sha512-nK99bvJiziGv/UOKJlDvFF45F00WgPLKVIGUfAK+mDhzVN2hb/S33uW2Tlhg5PVBoqY7tDVqL0zmu4OxAHgo9g==", + "optional": true, + "dependencies": { + "@smithy/service-error-classification": "^3.0.0", + "@smithy/types": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.0.1.tgz", + "integrity": "sha512-7F7VNNhAsfMRA8I986YdOY5fE0/T1/ZjFF6OLsqkvQVNP3vZ/szYDfGCyphb7ioA09r32K/0qbSFfNFU68aSzA==", + "optional": true, + "dependencies": { + "@smithy/fetch-http-handler": "^3.0.1", + "@smithy/node-http-handler": "^3.0.0", + "@smithy/types": "^3.0.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "optional": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "optional": true, + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bson": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.2.0.tgz", + "integrity": "sha512-ELCPqAdroMdcuxqwMgUpifQyRoTpyYCNr1V9xKyF40VsBobsj+BbWNRvwGchMgBPGqkw655ypkjj2MEF5ywVwg==", + "deprecated": "This is a stub types definition. bson provides its own type definitions, so you do not need this installed.", + "dependencies": { + "bson": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.0.tgz", + "integrity": "sha512-bGyep3JqPCRry1wq+O5n7oiBgGWmeIJXPjXXCo8EK0u8duZGSYar7cGqd3ML2JUsLGeB7fmc06KYo9fLGWqPvQ==", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" + }, + "node_modules/@types/mongodb": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz", + "integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==", + "dependencies": { + "@types/bson": "*", + "@types/node": "*" + } + }, + "node_modules/@types/multer": { + "version": "1.4.11", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz", + "integrity": "sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/node": { + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/pump": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@types/pump/-/pump-1.1.3.tgz", + "integrity": "sha512-ZyooTTivmOwPfOwLVaszkF8Zq6mvavgjuHYitZhrIjfQAJDH+kIP3N+MzpG1zDAslsHvVz6Q8ECfivix3qLJaQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "optional": true + }, + "node_modules/bson": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", + "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", + "dependencies": { + "buffer": "^5.6.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/busboy": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", + "integrity": "sha512-InWFDomvlkEj+xWLBfU3AvnbVYqeTWmQopiW0tWWEy5yehYm2YkGEc59sUmw/4ty5Zj/b0WHGs1LgecuBSBGrg==", + "dependencies": { + "dicer": "0.2.5", + "readable-stream": "1.1.x" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dicer": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", + "integrity": "sha512-FDvbtnq7dzlPz0wyYlOExifDEZcu8h+rErEXgfxqmLfRfC/kJidEFh4+effJRO3P0xmfqyPbSMG0LveNRfTKVg==", + "dependencies": { + "readable-stream": "1.1.x", + "streamsearch": "0.1.2" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/docxtemplater": { + "version": "3.47.3", + "resolved": "https://registry.npmjs.org/docxtemplater/-/docxtemplater-3.47.3.tgz", + "integrity": "sha512-WQUbSJIAS+vSxRHWpx8UXrb2CQiNUqtnPvtR6r/nYSfMX+Fwmv7WksuTgZx0Dn2IvgVFvMk3QVt3EMLlW4DVOw==", + "dependencies": { + "@xmldom/xmldom": "^0.8.10" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/elementtree": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/elementtree/-/elementtree-0.1.7.tgz", + "integrity": "sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==", + "dependencies": { + "sax": "1.1.4" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "funding": [ + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + }, + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "optional": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-own-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", + "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/image-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.1.1.tgz", + "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-generator": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-generator/-/is-generator-1.0.3.tgz", + "integrity": "sha512-G56jBpbJeg7ds83HW1LuShNs8J73Fv3CPz/bmROHOHlnKkN8sWb9ujiagjmxxMUywftgq48HlBZELKKqFLk0oA==" + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kareem": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", + "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/libreoffice-convert": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/libreoffice-convert/-/libreoffice-convert-1.5.1.tgz", + "integrity": "sha512-NtoH+OLl+ATuQE/HKPcpMgeJswwmJuvxZVzZ/+0kMZ3IxhCua8wdNeHqKelj36/2z7P595qjCNvQ2518gg8tvA==", + "dependencies": { + "async": "^3.2.3", + "tmp": "^0.2.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.45", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", + "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mongodb": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.17.2.tgz", + "integrity": "sha512-mLV7SEiov2LHleRJPMPrK2PMyhXFZt2UQLC4VD4pnth3jMjYKHhtqfwwkkvS/NXuo/Fp3vbhaNcXrIDaLRb9Tg==", + "dependencies": { + "bson": "^4.7.2", + "mongodb-connection-string-url": "^2.6.0", + "socks": "^2.7.1" + }, + "engines": { + "node": ">=12.9.0" + }, + "optionalDependencies": { + "@aws-sdk/credential-providers": "^3.186.0", + "@mongodb-js/saslprep": "^1.1.0" + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "dependencies": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + } + }, + "node_modules/mongodb-uri": { + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/mongodb-uri/-/mongodb-uri-0.9.7.tgz", + "integrity": "sha512-s6BdnqNoEYfViPJgkH85X5Nw5NpzxN8hoflKLweNa7vBxt2V7kaS06d74pAtqDxde8fn4r9h4dNdLiFGoNV0KA==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/mongoose": { + "version": "6.12.8", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-6.12.8.tgz", + "integrity": "sha512-/9KOOVq1a4XNzqcWiWoOckvCE9eJPQ4M6rA8BmGTeuyi/w8t7F7LZ+8Lv3yilcqV5JP78SfMmgzm4YqlGl3fOg==", + "dependencies": { + "bson": "^4.7.2", + "kareem": "2.5.1", + "mongodb": "4.17.2", + "mpath": "0.9.0", + "mquery": "4.0.3", + "ms": "2.1.3", + "sift": "16.0.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose-unique-validator": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mongoose-unique-validator/-/mongoose-unique-validator-3.1.0.tgz", + "integrity": "sha512-UsBBlFapip8gc8x1h+nLWnkOy+GTy9Z+zmTyZ35icLV3EoLIVz180vJzepfMM9yBy2AJh+maeuoM8CWtqejGUg==", + "dependencies": { + "lodash.foreach": "^4.1.0", + "lodash.get": "^4.0.2", + "lodash.merge": "^4.6.2" + }, + "peerDependencies": { + "mongoose": "^6.0.0" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-4.0.3.tgz", + "integrity": "sha512-J5heI+P08I6VJ2Ky3+33IpCdAvlYGTSUjwTPxkAr8i8EoduPMBX2OY/wa3IKZIQl7MU4SbFk8ndgSKyB/cl1zA==", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/mquery/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mquery/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/multer": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz", + "integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==", + "deprecated": "Multer 1.x is affected by CVE-2022-24434. This is fixed in v1.4.4-lts.1 which drops support for versions of Node.js before 6. Please upgrade to at least Node.js 6 and version 1.4.4-lts.1 of Multer. If you need support for older versions of Node.js, we are open to accepting patches that would fix the CVE on the main 1.x release line, whilst maintaining compatibility with Node.js 0.10.", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^0.2.11", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "on-finished": "^2.3.0", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/multer-gridfs-storage": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/multer-gridfs-storage/-/multer-gridfs-storage-5.0.2.tgz", + "integrity": "sha512-oYl70i792uyJfgpvOfJrZIru4MsjjAueDHLZXTDGix/yPJuk1/lfqdPHHnv/XVVGfVZb4G9jJqwEFf9JIX1SOQ==", + "dependencies": { + "@types/express": "^4.17.6", + "@types/mongodb": "^3.5.25", + "@types/multer": "^1.4.3", + "@types/pump": "^1.1.0", + "has-own-prop": "^2.0.0", + "is-generator": "^1.0.3", + "is-promise": "^4.0.0", + "lodash.isplainobject": ">=0.8.0", + "mongodb": ">=2", + "mongodb-uri": "^0.9.7", + "pump": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "multer": "^1.4.2" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/pizzip": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/pizzip/-/pizzip-3.1.7.tgz", + "integrity": "sha512-VemVeAQtdIA74AN1Fsd5OmbMbEeS4YOwwlcudgzvmUrOIOPrk1idYC5Tw5FUFq/I0c26ziNOw9z//iPmGfp1jA==", + "dependencies": { + "pako": "^2.1.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "dependencies": { + "inherits": "~2.0.3" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sax": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz", + "integrity": "sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==" + }, + "node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sift": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "optional": true, + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", + "integrity": "sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==" + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "optional": true + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/xlsx-template": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/xlsx-template/-/xlsx-template-1.4.4.tgz", + "integrity": "sha512-L6FRDs9i3Zzy8Qu3G8phLDi8AwzTwUwBptGFjQ3NaE4tL4iZ+8nPPgzYJbi+zzKpppbp3pH68n6SwZiU8d2qHQ==", + "dependencies": { + "@kant2002/jszip": "2.7.1", + "elementtree": "^0.1.7", + "image-size": "^1.0.2" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..2dc316f --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "worldshine", + "version": "1.0.0", + "description": "World Shine", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [ + "nodejs", + "express", + "mongodb", + "rest", + "api" + ], + "author": "yangli", + "license": "ISC", + "dependencies": { + "axios": "^0.27.2", + "bcryptjs": "^2.4.3", + "body-parser": "^1.20.0", + "cors": "^2.8.5", + "docxtemplater": "^3.47.1", + "express": "^4.18.1", + "jsonwebtoken": "^8.5.1", + "libreoffice-convert": "^1.0.5", + "moment": "^2.30.1", + "moment-timezone": "^0.5.45", + "mongoose": "^6.3.2", + "mongoose-unique-validator": "^3.0.0", + "multer": "^1.4.4", + "multer-gridfs-storage": "^5.0.2", + "pizzip": "^3.1.7", + "xlsx-template": "^1.4.4" + } +} diff --git a/server.js b/server.js new file mode 100644 index 0000000..ed287a9 --- /dev/null +++ b/server.js @@ -0,0 +1,219 @@ +const express = require("express"); +const bodyParser = require("body-parser"); +const cors = require("cors"); +const app = express(); +var corsOptions = { + origin: '*' +}; + +// const path = __dirname + '/client/build/'; +const path = __dirname + '/app/views/' +app.use(cors(corsOptions)); +// parse requests of content-type - application/json +app.use(bodyParser.json()); +app.use(express.static(path)); +// parse requests of content-type - application/x-www-form-urlencoded +app.use(bodyParser.urlencoded({ extended: true })); +const db = require('./app/models'); +db.mongoose + .connect(db.url, { + useNewUrlParser: true, + useUnifiedTopology: true + }) + .then(() => { + console.log("Connected to the database!"); + }) + .catch(err => { + console.log("Cannot connect to the database!", err); + process.exit(); + }); +// simple route +app.get('/', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/login', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/admin', function(req, res) { + res.sendFile(path + "index.html"); +}) +app.get('/admin/customer-report', function(req, res) { + res.sendFile(path + "index.html"); +}) +app.get('/trans-routes', function(req, res) { + res.sendFile(path + "index.html"); +}) +app.get('/trans-routes/dashboard', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/trans-routes/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/trans-routes/edit/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/trans-routes/schedule', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/trans-routes/route-report-with-signature/:id', function (req, res) { + res.sendFile(path + "index.html"); +}); +app.get('/trans-routes/create', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/trans-routes/templates', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/trans-routes/route-signature', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/trans-routes/templates/edit/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/employees', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/vehicles', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/vehicles/edit/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/vehicles/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/vehicles/list', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/messages', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/messages/edit/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/messages/list', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/message-tokens', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/messages/send-message', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/messages/sent-messages/list', function(req, res) { + res.sendFile(path + "index.html"); +}); +app.get('/center-phones', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/center-phones/edit/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/center-phones/list', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/employees/list', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/employees/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/employees/edit/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/customers/list', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/customers/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/customers/edit/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/customers', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/landing', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/users', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/medical', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/medical/index', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/medical/events/list', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/medical/events', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/medical/events/request', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/medical/events/request/list', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/medical/events/edit/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('events/create-from-request', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/medical/events/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/medical/events/multiple-list', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/medical/resources/list', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/medical/events/calendar', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/medical/resources', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/medical/resources/edit/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/medical/resources/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +app.get('/signature/:id', function (req,res) { + res.sendFile(path + "index.html"); +}); +require("./app/routes/user.routes")(app); +require("./app/routes/auth.routes")(app); +require("./app/routes/employee.routes")(app); +require("./app/routes/vehicle.routes")(app); +require("./app/routes/customer.routes")(app); +require("./app/routes/client.routes")(app); +require("./app/routes/staff.routes")(app); +require("./app/routes/route-path.routes")(app); +require("./app/routes/route-path-template.routes")(app); +require("./app/routes/upload.routes")(app); +require("./app/routes/report.routes")(app); +require("./app/routes/message.routes")(app); +require("./app/routes/resource.routes")(app); +require("./app/routes/center-phone.routes")(app); +require("./app/routes/message-token.routes")(app); +require("./app/routes/calendar-event.route")(app); +require("./app/routes/doctemplate.route")(app); +require("./app/routes/xlsxtemplate.route")(app); +require("./app/routes/timedata.routes")(app); +require("./app/routes/breakfast.routes")(app); +require("./app/routes/event-request.routes")(app); +require("./app/routes/signature-request.routes")(app); +require("./app/routes/snack.routes")(app); +require("./app/routes/lunch.routes")(app); + +// set port, listen for requests +const PORT = process.env.PORT || 8080; +app.listen(PORT, () => { + console.log(`Server is running on port ${PORT}.`); +}); \ No newline at end of file