changes
This commit is contained in:
@@ -244,14 +244,15 @@ class FusionClockAPI(http.Controller):
|
||||
|
||||
location, distance, err, method = self._verify_location(latitude, longitude, employee, client_ip)
|
||||
if not location:
|
||||
# Log geofence violation
|
||||
err_msg = self._location_error_message(err, distance)
|
||||
err_msg += f" (Your IP: {client_ip})"
|
||||
self._log_activity(
|
||||
employee, 'outside_geofence',
|
||||
self._location_error_message(err, distance),
|
||||
err_msg,
|
||||
latitude=latitude, longitude=longitude, distance=distance, source=source,
|
||||
)
|
||||
return {
|
||||
'error': self._location_error_message(err, distance),
|
||||
'error': err_msg,
|
||||
'allowed': False,
|
||||
'error_type': err,
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import logging
|
||||
import requests
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.http import request as http_request
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -126,16 +127,18 @@ class FusionClockLocation(models.Model):
|
||||
return False
|
||||
|
||||
def action_detect_ip(self):
|
||||
"""Detect the current public IP and append it to the whitelist."""
|
||||
"""Detect the IP the Odoo server sees from your browser and add it."""
|
||||
self.ensure_one()
|
||||
try:
|
||||
resp = requests.get('https://ipapi.co/json/', timeout=10)
|
||||
data = resp.json()
|
||||
ip = data.get('ip', '')
|
||||
ip = ''
|
||||
if http_request:
|
||||
ip = http_request.httprequest.environ.get(
|
||||
'HTTP_X_FORWARDED_FOR', ''
|
||||
).split(',')[0].strip()
|
||||
if not ip:
|
||||
raise UserError(_("Could not detect public IP."))
|
||||
except requests.exceptions.RequestException as e:
|
||||
raise UserError(_("Network error detecting IP: %s") % str(e))
|
||||
ip = http_request.httprequest.remote_addr or ''
|
||||
|
||||
if not ip:
|
||||
raise UserError(_("Could not detect your IP from the request."))
|
||||
|
||||
existing = (self.ip_whitelist or '').strip()
|
||||
existing_lines = [l.strip() for l in existing.split('\n') if l.strip()] if existing else []
|
||||
@@ -145,7 +148,7 @@ class FusionClockLocation(models.Model):
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'title': _('Already Whitelisted'),
|
||||
'message': _('%s is already in the whitelist.') % ip,
|
||||
'message': _('IP %s is already in the whitelist.') % ip,
|
||||
'type': 'warning',
|
||||
'sticky': False,
|
||||
},
|
||||
@@ -153,20 +156,25 @@ class FusionClockLocation(models.Model):
|
||||
|
||||
existing_lines.append(ip)
|
||||
self.ip_whitelist = '\n'.join(existing_lines)
|
||||
city = data.get('city', '')
|
||||
org = data.get('org', '')
|
||||
detail = f"{ip}"
|
||||
if city:
|
||||
detail += f" ({city}"
|
||||
if org:
|
||||
detail += f" - {org}"
|
||||
detail += ")"
|
||||
|
||||
extra = ''
|
||||
try:
|
||||
resp = requests.get(f'https://ipapi.co/{ip}/json/', timeout=5)
|
||||
if resp.ok:
|
||||
data = resp.json()
|
||||
city = data.get('city', '')
|
||||
org = data.get('org', '')
|
||||
if city or org:
|
||||
extra = f" ({', '.join(filter(None, [city, org]))})"
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'title': _('IP Detected & Added'),
|
||||
'message': _('Added %s to the whitelist.') % detail,
|
||||
'message': _('Added %s%s to the whitelist. This is the IP the server sees from your browser.') % (ip, extra),
|
||||
'type': 'success',
|
||||
'sticky': False,
|
||||
},
|
||||
|
||||
@@ -170,17 +170,79 @@ class FusionClockReport(models.Model):
|
||||
})
|
||||
|
||||
def _send_report_email(self):
|
||||
"""Send the report via mail template."""
|
||||
"""Send the report with the PDF attached."""
|
||||
self.ensure_one()
|
||||
if self.employee_id:
|
||||
template = self.env.ref('fusion_clock.mail_template_clock_employee_report', raise_if_not_found=False)
|
||||
else:
|
||||
template = self.env.ref('fusion_clock.mail_template_clock_batch_report', raise_if_not_found=False)
|
||||
company_email = self.company_id.email or ''
|
||||
|
||||
if template:
|
||||
template.send_mail(self.id, force_send=True)
|
||||
if self.employee_id:
|
||||
email_to = self.employee_id.work_email or ''
|
||||
subject = f"Your Attendance Report - {self.date_start} to {self.date_end}"
|
||||
body = (
|
||||
'<div style="margin:0;padding:0;font-family:Arial,Helvetica,sans-serif;">'
|
||||
'<table width="600" style="margin:0 auto;background:#ffffff;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden;">'
|
||||
'<tr><td style="padding:24px 32px;background:#1a1d23;">'
|
||||
'<h2 style="color:#10B981;margin:0;">Fusion Clock</h2>'
|
||||
'<p style="color:#9ca3af;margin:4px 0 0;">Attendance Report</p>'
|
||||
'</td></tr><tr><td style="padding:24px 32px;">'
|
||||
f'<p>Hello <strong>{self.employee_id.name}</strong>,</p>'
|
||||
f'<p>Your attendance report for <strong>{self.date_start}</strong> to '
|
||||
f'<strong>{self.date_end}</strong> is ready.</p>'
|
||||
'<table width="100%" style="margin:16px 0;border-collapse:collapse;">'
|
||||
f'<tr style="background:#f8f9fa;"><td style="padding:10px 14px;border:1px solid #e0e0e0;font-weight:600;">Days Worked</td>'
|
||||
f'<td style="padding:10px 14px;border:1px solid #e0e0e0;">{self.days_worked}</td></tr>'
|
||||
f'<tr><td style="padding:10px 14px;border:1px solid #e0e0e0;font-weight:600;">Total Hours</td>'
|
||||
f'<td style="padding:10px 14px;border:1px solid #e0e0e0;">{self.total_hours:.1f}h</td></tr>'
|
||||
f'<tr style="background:#f8f9fa;"><td style="padding:10px 14px;border:1px solid #e0e0e0;font-weight:600;">Net Hours</td>'
|
||||
f'<td style="padding:10px 14px;border:1px solid #e0e0e0;">{self.net_hours:.1f}h</td></tr>'
|
||||
f'<tr><td style="padding:10px 14px;border:1px solid #e0e0e0;font-weight:600;">Total Breaks</td>'
|
||||
f'<td style="padding:10px 14px;border:1px solid #e0e0e0;">{self.total_breaks:.0f} min</td></tr>'
|
||||
'</table>'
|
||||
'<p>The full PDF report is attached.</p>'
|
||||
'<p style="color:#6b7280;font-size:12px;margin-top:16px;">This is an automated message from Fusion Clock.</p>'
|
||||
'</td></tr></table></div>'
|
||||
)
|
||||
else:
|
||||
_logger.warning("Fusion Clock: Mail template not found for report %s", self.id)
|
||||
ICP = self.env['ir.config_parameter'].sudo()
|
||||
email_to = ICP.get_param('fusion_clock.report_recipient_emails', '')
|
||||
subject = f"Employee Attendance Batch Report - {self.date_start} to {self.date_end}"
|
||||
body = (
|
||||
'<div style="margin:0;padding:0;font-family:Arial,Helvetica,sans-serif;">'
|
||||
'<table width="600" style="margin:0 auto;background:#ffffff;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden;">'
|
||||
'<tr><td style="padding:24px 32px;background:#1a1d23;">'
|
||||
'<h2 style="color:#10B981;margin:0;">Fusion Clock</h2>'
|
||||
'<p style="color:#9ca3af;margin:4px 0 0;">Batch Attendance Report</p>'
|
||||
'</td></tr><tr><td style="padding:24px 32px;">'
|
||||
f'<p>The attendance batch report for <strong>{self.date_start}</strong> to '
|
||||
f'<strong>{self.date_end}</strong> is attached.</p>'
|
||||
'<p>This report includes all employees\' attendance summaries with daily breakdowns, '
|
||||
'total hours, and penalty information.</p>'
|
||||
'<p style="color:#6b7280;font-size:12px;margin-top:16px;">This is an automated message from Fusion Clock.</p>'
|
||||
'</td></tr></table></div>'
|
||||
)
|
||||
|
||||
if not email_to:
|
||||
_logger.warning("Fusion Clock: No email recipient for report %s", self.id)
|
||||
return
|
||||
|
||||
mail_vals = {
|
||||
'subject': subject,
|
||||
'email_from': company_email,
|
||||
'email_to': email_to,
|
||||
'body_html': body,
|
||||
'auto_delete': True,
|
||||
}
|
||||
|
||||
if self.report_pdf:
|
||||
mail_vals['attachment_ids'] = [(0, 0, {
|
||||
'name': self.report_pdf_filename or 'report.pdf',
|
||||
'datas': self.report_pdf,
|
||||
'mimetype': 'application/pdf',
|
||||
})]
|
||||
|
||||
try:
|
||||
self.env['mail.mail'].sudo().create(mail_vals).send()
|
||||
except Exception as e:
|
||||
_logger.error("Fusion Clock: Failed to send report email: %s", e)
|
||||
|
||||
def action_export_csv(self):
|
||||
"""Export the report data as a CSV file for payroll."""
|
||||
|
||||
@@ -360,7 +360,7 @@ class HrAttendance(models.Model):
|
||||
('x_fclk_enable_clock', '=', True),
|
||||
])
|
||||
|
||||
template = self.env.ref('fusion_clock.mail_template_weekly_summary', raise_if_not_found=False)
|
||||
company_email = self.env.company.email or ''
|
||||
|
||||
for emp in employees:
|
||||
if not emp.work_email:
|
||||
@@ -373,35 +373,66 @@ class HrAttendance(models.Model):
|
||||
('check_out', '!=', False),
|
||||
])
|
||||
|
||||
total_net = sum(a.x_fclk_net_hours or 0 for a in atts)
|
||||
total_ot = sum(a.x_fclk_overtime_hours or 0 for a in atts)
|
||||
penalties = self.env['fusion.clock.penalty'].sudo().search_count([
|
||||
total_net = round(sum(a.x_fclk_net_hours or 0 for a in atts), 1)
|
||||
total_ot = round(sum(a.x_fclk_overtime_hours or 0 for a in atts), 1)
|
||||
penalty_count = self.env['fusion.clock.penalty'].sudo().search_count([
|
||||
('employee_id', '=', emp.id),
|
||||
('date', '>=', week_start),
|
||||
('date', '<=', week_end),
|
||||
])
|
||||
|
||||
ActivityLog = self.env['fusion.clock.activity.log'].sudo()
|
||||
absences = ActivityLog.search_count([
|
||||
absence_count = ActivityLog.search_count([
|
||||
('employee_id', '=', emp.id),
|
||||
('log_type', '=', 'absent'),
|
||||
('log_date', '>=', datetime.combine(week_start, datetime.min.time())),
|
||||
('log_date', '<', datetime.combine(week_end + timedelta(days=1), datetime.min.time())),
|
||||
])
|
||||
|
||||
if template:
|
||||
try:
|
||||
template.with_context(
|
||||
week_start=week_start,
|
||||
week_end=week_end,
|
||||
total_hours=round(total_net, 1),
|
||||
overtime_hours=round(total_ot, 1),
|
||||
penalty_count=penalties,
|
||||
absence_count=absences,
|
||||
streak=emp.x_fclk_ontime_streak,
|
||||
).send_mail(emp.id, force_send=False)
|
||||
except Exception as e:
|
||||
_logger.error("Fusion Clock: Failed to send weekly summary to %s: %s", emp.name, e)
|
||||
streak = emp.x_fclk_ontime_streak or 0
|
||||
|
||||
def _row(label, value, bg=False):
|
||||
bg_style = 'background:#f8f9fa;' if bg else ''
|
||||
return (
|
||||
f'<tr style="{bg_style}">'
|
||||
f'<td style="padding:10px 14px;border:1px solid #e0e0e0;font-weight:600;">{label}</td>'
|
||||
f'<td style="padding:10px 14px;border:1px solid #e0e0e0;">{value}</td>'
|
||||
f'</tr>'
|
||||
)
|
||||
|
||||
body = (
|
||||
'<div style="margin:0;padding:0;font-family:Arial,Helvetica,sans-serif;">'
|
||||
'<table width="600" style="margin:0 auto;background:#ffffff;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden;">'
|
||||
'<tr><td style="padding:24px 32px;background:#1a1d23;border-radius:8px 8px 0 0;">'
|
||||
'<h2 style="color:#10B981;margin:0;">Fusion Clock</h2>'
|
||||
'<p style="color:#9ca3af;margin:4px 0 0;">Weekly Summary</p>'
|
||||
'</td></tr>'
|
||||
'<tr><td style="padding:24px 32px;">'
|
||||
f'<p>Hello <strong>{emp.name}</strong>,</p>'
|
||||
f'<p>Here is your attendance summary for <strong>{week_start}</strong> to <strong>{week_end}</strong>:</p>'
|
||||
'<table width="100%" style="margin:16px 0;border-collapse:collapse;">'
|
||||
+ _row('Total Hours', f'{total_net}h', True)
|
||||
+ _row('Overtime', f'{total_ot}h', False)
|
||||
+ _row('Penalties', str(penalty_count), True)
|
||||
+ _row('Absences', str(absence_count), False)
|
||||
+ _row('On-Time Streak', f'{streak} days', True)
|
||||
+ '</table>'
|
||||
'<p>Log in to <a href="/my/clock" style="color:#10B981;">your portal</a> to view details.</p>'
|
||||
'<p style="color:#6b7280;font-size:12px;margin-top:16px;">This is an automated message from Fusion Clock.</p>'
|
||||
'</td></tr></table></div>'
|
||||
)
|
||||
|
||||
try:
|
||||
mail = self.env['mail.mail'].sudo().create({
|
||||
'subject': f'Your Weekly Attendance Summary ({week_start} - {week_end})',
|
||||
'email_from': company_email,
|
||||
'email_to': emp.work_email,
|
||||
'body_html': body,
|
||||
'auto_delete': True,
|
||||
})
|
||||
mail.send()
|
||||
except Exception as e:
|
||||
_logger.error("Fusion Clock: Failed to send weekly summary to %s: %s", emp.name, e)
|
||||
|
||||
@api.model
|
||||
def _fclk_notify_office(self, office_user_id, summary, note, res_model, res_id):
|
||||
|
||||
@@ -66,12 +66,23 @@ html.o_dark .fclk-app,
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Hide portal navigation */
|
||||
/* Hide portal navigation and footer */
|
||||
.fclk-app ~ .o_portal_navbar,
|
||||
.fclk-app .o_portal_navbar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.fclk-app ~ footer,
|
||||
.fclk-app ~ .o_footer,
|
||||
.fclk-app ~ .o_footer_copyright {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
body:has(.fclk-app) footer,
|
||||
body:has(.fclk-app) .o_footer {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.fclk-container {
|
||||
max-width: 480px;
|
||||
margin: 0 auto;
|
||||
@@ -224,11 +235,12 @@ html.o_dark .fclk-app,
|
||||
|
||||
.fclk-timer {
|
||||
color: var(--fclk-text);
|
||||
font-size: 52px;
|
||||
font-size: clamp(32px, 10vw, 52px);
|
||||
font-weight: 300;
|
||||
letter-spacing: 4px;
|
||||
letter-spacing: 2px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-family: 'SF Mono', 'Fira Code', 'Courier New', monospace;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ---- Clock Button ---- */
|
||||
@@ -282,6 +294,13 @@ html.o_dark .fclk-app,
|
||||
.fclk-btn-icon {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.fclk-btn-icon svg[id="fclk-btn-icon-play"] {
|
||||
transform: translateX(3px);
|
||||
}
|
||||
|
||||
.fclk-btn-ripple {
|
||||
@@ -1000,14 +1019,15 @@ html.o_dark .fclk-wizard-overlay {
|
||||
}
|
||||
|
||||
/* ---- Responsive ---- */
|
||||
@media (max-width: 380px) {
|
||||
.fclk-timer {
|
||||
font-size: 40px;
|
||||
}
|
||||
@media (max-width: 420px) {
|
||||
.fclk-clock-btn {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
.fclk-clock-btn svg {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
.fclk-stat-value {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,13 @@ export class FusionClockPortal extends Interaction {
|
||||
this.checkInTime = new Date(this.el.dataset.checkInTime + "Z");
|
||||
}
|
||||
|
||||
// If server didn't provide check-in data, try localStorage
|
||||
if (!this.isCheckedIn || !this.checkInTime) {
|
||||
this._restoreState();
|
||||
} else {
|
||||
this._saveState();
|
||||
}
|
||||
|
||||
// Load locations
|
||||
const locDataEl = document.getElementById("fclk-locations-data");
|
||||
this.locations = [];
|
||||
@@ -34,9 +41,6 @@ export class FusionClockPortal extends Interaction {
|
||||
this.selectedLocationId = this.locations[0].id;
|
||||
}
|
||||
|
||||
// Restore localStorage state
|
||||
this._restoreState();
|
||||
|
||||
// Start live clock
|
||||
this._updateCurrentTime();
|
||||
this.clockInterval = setInterval(() => this._updateCurrentTime(), 1000);
|
||||
@@ -44,6 +48,9 @@ export class FusionClockPortal extends Interaction {
|
||||
// Start timer if checked in
|
||||
if (this.isCheckedIn && this.checkInTime) {
|
||||
this._startTimer();
|
||||
this._updateUIForClockIn({
|
||||
location_name: this.el.dataset.locationName || "",
|
||||
});
|
||||
}
|
||||
|
||||
this._updateDateDisplay();
|
||||
@@ -51,14 +58,16 @@ export class FusionClockPortal extends Interaction {
|
||||
// Event listeners
|
||||
this._setupEventListeners();
|
||||
|
||||
// Visibility sync
|
||||
// Visibility sync -- also sync on load to verify with server
|
||||
this._onVisibilityChange = () => this._syncOnVisibilityChange();
|
||||
document.addEventListener("visibilitychange", this._onVisibilityChange);
|
||||
this._syncTimeout = setTimeout(() => this._syncOnVisibilityChange(), 3000);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._stopTimer();
|
||||
if (this.clockInterval) clearInterval(this.clockInterval);
|
||||
if (this._syncTimeout) clearTimeout(this._syncTimeout);
|
||||
if (this._onVisibilityChange) {
|
||||
document.removeEventListener("visibilitychange", this._onVisibilityChange);
|
||||
}
|
||||
@@ -247,6 +256,7 @@ export class FusionClockPortal extends Interaction {
|
||||
if (result.action === "clock_in") {
|
||||
this.isCheckedIn = true;
|
||||
this.checkInTime = new Date(result.check_in + "Z");
|
||||
this.el.dataset.locationName = result.location_name || "";
|
||||
this._updateUIForClockIn(result);
|
||||
this._startTimer();
|
||||
this._playSound("in");
|
||||
@@ -483,6 +493,9 @@ export class FusionClockPortal extends Interaction {
|
||||
if (this.checkInTime) {
|
||||
localStorage.setItem("fclk_check_in_time", this.checkInTime.toISOString());
|
||||
}
|
||||
if (this.el.dataset.locationName) {
|
||||
localStorage.setItem("fclk_location_name", this.el.dataset.locationName);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
@@ -490,13 +503,21 @@ export class FusionClockPortal extends Interaction {
|
||||
try {
|
||||
localStorage.removeItem("fclk_checked_in");
|
||||
localStorage.removeItem("fclk_check_in_time");
|
||||
localStorage.removeItem("fclk_location_name");
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
_restoreState() {
|
||||
try {
|
||||
if (!this.isCheckedIn && localStorage.getItem("fclk_checked_in") === "true") {
|
||||
this._clearState();
|
||||
const wasCheckedIn = localStorage.getItem("fclk_checked_in") === "true";
|
||||
const savedTime = localStorage.getItem("fclk_check_in_time");
|
||||
if (wasCheckedIn && savedTime) {
|
||||
const t = new Date(savedTime);
|
||||
if (!isNaN(t.getTime()) && (Date.now() - t.getTime()) < 24 * 60 * 60 * 1000) {
|
||||
this.isCheckedIn = true;
|
||||
this.checkInTime = t;
|
||||
this.el.dataset.locationName = localStorage.getItem("fclk_location_name") || "";
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
@@ -587,6 +608,12 @@ export class FusionClockPortal extends Interaction {
|
||||
this._updateUIForClockIn({ location_name: result.location_name });
|
||||
this._startTimer();
|
||||
this._saveState();
|
||||
} else if (result.is_checked_in && this.isCheckedIn) {
|
||||
const serverTime = new Date(result.check_in + "Z");
|
||||
if (Math.abs(serverTime - this.checkInTime) > 5000) {
|
||||
this.checkInTime = serverTime;
|
||||
this._saveState();
|
||||
}
|
||||
} else if (!result.is_checked_in && this.isCheckedIn) {
|
||||
this.isCheckedIn = false;
|
||||
this._updateUIForClockOut({});
|
||||
@@ -602,7 +629,9 @@ export class FusionClockPortal extends Interaction {
|
||||
if (weekEl && result.week_hours !== undefined) {
|
||||
weekEl.textContent = result.week_hours.toFixed(1) + "h";
|
||||
}
|
||||
} catch (e) {}
|
||||
} catch {
|
||||
// Server unavailable (restarting) -- keep timer running from localStorage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,26 +76,44 @@ export class FusionClockFAB extends Component {
|
||||
async _fetchStatus() {
|
||||
try {
|
||||
const result = await rpc("/fusion_clock/get_status", {});
|
||||
if (result.error) {
|
||||
this.state.isDisplayed = false;
|
||||
return;
|
||||
}
|
||||
if (result.error) return;
|
||||
|
||||
this.state.isDisplayed = result.enable_clock !== false;
|
||||
this.state.isCheckedIn = result.is_checked_in;
|
||||
this.state.locationName = result.location_name || "";
|
||||
this.state.todayHours = (result.today_hours || 0).toFixed(1);
|
||||
this.state.weekHours = (result.week_hours || 0).toFixed(1);
|
||||
|
||||
if (result.is_checked_in && result.check_in) {
|
||||
this.state.checkInTime = new Date(result.check_in + "Z");
|
||||
this._startTimer();
|
||||
} else {
|
||||
const serverTime = new Date(result.check_in + "Z");
|
||||
const wasRunning = this.state.isCheckedIn;
|
||||
this.state.isCheckedIn = true;
|
||||
if (!wasRunning || Math.abs(serverTime - this.state.checkInTime) > 5000) {
|
||||
this.state.checkInTime = serverTime;
|
||||
this._startTimer();
|
||||
}
|
||||
try { localStorage.setItem("fclk_fab_check_in", serverTime.toISOString()); } catch {}
|
||||
} else if (!result.is_checked_in) {
|
||||
this.state.isCheckedIn = false;
|
||||
this.state.checkInTime = null;
|
||||
this._stopTimer();
|
||||
this.state.timerDisplay = "00:00:00";
|
||||
try { localStorage.removeItem("fclk_fab_check_in"); } catch {}
|
||||
}
|
||||
} catch {
|
||||
if (!this.state.isCheckedIn) {
|
||||
try {
|
||||
const saved = localStorage.getItem("fclk_fab_check_in");
|
||||
if (saved) {
|
||||
const t = new Date(saved);
|
||||
if (!isNaN(t.getTime()) && (Date.now() - t.getTime()) < 24 * 60 * 60 * 1000) {
|
||||
this.state.isDisplayed = true;
|
||||
this.state.isCheckedIn = true;
|
||||
this.state.checkInTime = t;
|
||||
this._startTimer();
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
} catch (e) {
|
||||
this.state.isDisplayed = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user