Initial commit
This commit is contained in:
344
fusion_clock/views/portal_clock_templates.xml
Normal file
344
fusion_clock/views/portal_clock_templates.xml
Normal file
@@ -0,0 +1,344 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Add Clock In/Out tile to the Fusion portal home grid -->
|
||||
<template id="portal_my_home_clock" name="Portal My Home: Clock"
|
||||
inherit_id="fusion_authorizer_portal.portal_my_home_authorizer" priority="60">
|
||||
<xpath expr="//div[hasclass('row')][hasclass('g-3')][hasclass('mb-4')]" position="inside">
|
||||
<t t-set="pfab_emp_home" t-value="request.env['hr.employee'].sudo().search([('user_id','=',request.env.uid)], limit=1)"/>
|
||||
<t t-if="pfab_emp_home and pfab_emp_home.x_fclk_enable_clock">
|
||||
<div class="col-md-6">
|
||||
<a href="/my/clock" class="card h-100 border-0 shadow-sm text-decoration-none" style="border-radius: 12px; min-height: 100px;">
|
||||
<div class="card-body d-flex align-items-center p-4">
|
||||
<div class="me-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px; background: linear-gradient(135deg, #0d9488 0%, #2563eb 100%);">
|
||||
<i class="fa fa-clock-o fa-lg text-white"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="mb-1 text-dark">Clock In / Out</h5>
|
||||
<small class="text-muted">Punch in, view timesheets, and reports</small>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- ================================================================
|
||||
Portal FAB - Floating clock button on ALL portal pages
|
||||
Injected into portal.portal_layout so it appears everywhere.
|
||||
================================================================ -->
|
||||
<template id="portal_layout_clock_fab" name="Portal Clock FAB"
|
||||
inherit_id="portal.portal_layout">
|
||||
<xpath expr="//div[@id='wrap']" position="after">
|
||||
<t t-set="pfab_emp" t-value="fclk_employee if fclk_employee is defined else False"/>
|
||||
<t t-if="pfab_emp and pfab_emp.x_fclk_enable_clock">
|
||||
<div id="fclk-portal-fab"
|
||||
t-att-data-checked-in="'true' if fclk_checked_in else 'false'"
|
||||
t-att-data-check-in-time="fclk_check_in_time or ''"
|
||||
t-att-data-location-name="fclk_location_name or ''">
|
||||
|
||||
<!-- Expandable panel (above button) -->
|
||||
<div class="fclk-pfab-panel" style="display:none;">
|
||||
<div class="fclk-pfab-panel-header">
|
||||
<div class="fclk-pfab-panel-title">
|
||||
<span class="fclk-pfab-status-dot"/>
|
||||
<span class="fclk-pfab-status-text">Ready</span>
|
||||
</div>
|
||||
<a href="/my/clock" class="fclk-pfab-open-link" title="Open Full Clock">
|
||||
<i class="fa fa-external-link"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="fclk-pfab-location" style="display:none;">
|
||||
<i class="fa fa-map-marker"/>
|
||||
<span class="fclk-pfab-location-text"/>
|
||||
</div>
|
||||
<div class="fclk-pfab-timer">00:00:00</div>
|
||||
<div class="fclk-pfab-stats">
|
||||
<div class="fclk-pfab-stat">
|
||||
<span class="fclk-pfab-stat-val fclk-pfab-today">0.0h</span>
|
||||
<span class="fclk-pfab-stat-lbl">Today</span>
|
||||
</div>
|
||||
<div class="fclk-pfab-stat-divider"/>
|
||||
<div class="fclk-pfab-stat">
|
||||
<span class="fclk-pfab-stat-val fclk-pfab-week">0.0h</span>
|
||||
<span class="fclk-pfab-stat-lbl">Week</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="fclk-pfab-action fclk-pfab-action--in">
|
||||
<i class="fa fa-play-circle-o"/> Clock In
|
||||
</button>
|
||||
<div class="fclk-pfab-error" style="display:none;">
|
||||
<i class="fa fa-exclamation-triangle"/>
|
||||
<span class="fclk-pfab-error-text"/>
|
||||
</div>
|
||||
<div class="fclk-pfab-panel-arrow"/>
|
||||
</div>
|
||||
|
||||
<!-- Floating button -->
|
||||
<button class="fclk-pfab-btn">
|
||||
<span class="fclk-pfab-ripple-ring fclk-pfab-ripple-ring--1"/>
|
||||
<span class="fclk-pfab-ripple-ring fclk-pfab-ripple-ring--2"/>
|
||||
<span class="fclk-pfab-ripple-ring fclk-pfab-ripple-ring--3"/>
|
||||
<span class="fclk-pfab-icon">
|
||||
<i class="fa fa-clock-o"/>
|
||||
</span>
|
||||
<span class="fclk-pfab-badge" style="display:none;">00:00:00</span>
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Main Clock Page -->
|
||||
<template id="portal_clock_page" name="Fusion Clock Portal">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="breadcrumbs_searchbar" t-value="False"/>
|
||||
<t t-set="no_breadcrumbs" t-value="True"/>
|
||||
<t t-set="no_header" t-value="True"/>
|
||||
|
||||
<div id="fusion-clock-app" class="fclk-app"
|
||||
t-att-data-checked-in="'true' if is_checked_in else 'false'"
|
||||
t-att-data-check-in-time="current_attendance.check_in.isoformat() if current_attendance and current_attendance.check_in else ''"
|
||||
t-att-data-location-name="current_attendance.x_fclk_location_id.name if current_attendance and current_attendance.x_fclk_location_id else ''"
|
||||
t-att-data-enable-sounds="'true' if enable_sounds else 'false'"
|
||||
t-att-data-google-maps-key="google_maps_key or ''">
|
||||
|
||||
<!-- Dark Background Container -->
|
||||
<div class="fclk-container">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="fclk-header">
|
||||
<div class="fclk-date" id="fclk-date-display"></div>
|
||||
<h1 class="fclk-greeting">Hello, <t t-esc="employee.name.split(' ')[0]"/></h1>
|
||||
</div>
|
||||
|
||||
<!-- Status Card -->
|
||||
<div class="fclk-status-card">
|
||||
<div class="fclk-status-row">
|
||||
<div class="fclk-status-indicator">
|
||||
<span class="fclk-dot" id="fclk-status-dot"
|
||||
t-attf-class="fclk-dot {{ 'fclk-dot-active' if is_checked_in else '' }}"></span>
|
||||
<span class="fclk-status-text" id="fclk-status-text">
|
||||
<t t-if="is_checked_in">Clocked In</t>
|
||||
<t t-else="">Not Clocked In</t>
|
||||
</span>
|
||||
</div>
|
||||
<span class="fclk-current-time" id="fclk-current-time"></span>
|
||||
</div>
|
||||
|
||||
<!-- Location Selector -->
|
||||
<div class="fclk-location-card" id="fclk-location-card">
|
||||
<div class="fclk-location-icon">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#10B981" stroke-width="2">
|
||||
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/>
|
||||
<circle cx="12" cy="10" r="3"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="fclk-location-info">
|
||||
<div class="fclk-location-name" id="fclk-location-name">
|
||||
<t t-if="locations">
|
||||
<t t-esc="locations[0].name"/>
|
||||
</t>
|
||||
<t t-else="">No locations configured</t>
|
||||
</div>
|
||||
<div class="fclk-location-address" id="fclk-location-address">
|
||||
<t t-if="locations">
|
||||
<t t-esc="locations[0].address or 'No address'"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fclk-location-arrow" t-if="len(locations) > 1">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#6b7280" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Timer Section -->
|
||||
<div class="fclk-timer-section">
|
||||
<div class="fclk-timer-label" id="fclk-timer-label">
|
||||
<t t-if="is_checked_in">Time Elapsed</t>
|
||||
<t t-else="">Ready to Clock In</t>
|
||||
</div>
|
||||
<div class="fclk-timer" id="fclk-timer">00:00:00</div>
|
||||
</div>
|
||||
|
||||
<!-- Clock Button -->
|
||||
<div class="fclk-button-section">
|
||||
<button class="fclk-clock-btn" id="fclk-clock-btn"
|
||||
t-attf-class="fclk-clock-btn {{ 'fclk-clock-btn-out' if is_checked_in else '' }}">
|
||||
<div class="fclk-btn-ripple"></div>
|
||||
<svg id="fclk-btn-icon-play" class="fclk-btn-icon"
|
||||
t-attf-style="display: {{ 'none' if is_checked_in else 'block' }}; margin-left: 4px;"
|
||||
width="40" height="40" viewBox="0 0 24 24" fill="white">
|
||||
<polygon points="6 3 20 12 6 21 6 3"/>
|
||||
</svg>
|
||||
<svg id="fclk-btn-icon-stop" class="fclk-btn-icon"
|
||||
t-attf-style="display: {{ 'block' if is_checked_in else 'none' }}"
|
||||
width="36" height="36" viewBox="0 0 24 24" fill="white">
|
||||
<rect x="4" y="4" width="16" height="16" rx="2"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="fclk-btn-label" id="fclk-btn-label">
|
||||
<t t-if="is_checked_in">Tap to Clock Out</t>
|
||||
<t t-else="">Tap to Clock In</t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats Cards -->
|
||||
<div class="fclk-stats-row">
|
||||
<div class="fclk-stat-card">
|
||||
<div class="fclk-stat-header">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#10B981" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<polyline points="12 6 12 12 16 14"/>
|
||||
</svg>
|
||||
<span>Today</span>
|
||||
</div>
|
||||
<div class="fclk-stat-value" id="fclk-today-hours">
|
||||
<t t-esc="'%.1f' % today_hours"/>h
|
||||
</div>
|
||||
<div class="fclk-stat-target">of 8h target</div>
|
||||
</div>
|
||||
<div class="fclk-stat-card">
|
||||
<div class="fclk-stat-header">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#10B981" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<polyline points="12 6 12 12 16 14"/>
|
||||
</svg>
|
||||
<span>This Week</span>
|
||||
</div>
|
||||
<div class="fclk-stat-value" id="fclk-week-hours">
|
||||
<t t-esc="'%.1f' % week_hours"/>h
|
||||
</div>
|
||||
<div class="fclk-stat-target">of 40h target</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Activity -->
|
||||
<div class="fclk-recent-section">
|
||||
<div class="fclk-recent-header">
|
||||
<h3>Recent Activity</h3>
|
||||
<a href="/my/clock/timesheets" class="fclk-view-all">View All</a>
|
||||
</div>
|
||||
<div class="fclk-recent-list" id="fclk-recent-list">
|
||||
<t t-foreach="recent_attendances" t-as="att">
|
||||
<div class="fclk-recent-item">
|
||||
<div class="fclk-recent-date">
|
||||
<div class="fclk-recent-day-name">
|
||||
<t t-esc="att.check_in.strftime('%a')"/>
|
||||
</div>
|
||||
<div class="fclk-recent-day-num">
|
||||
<t t-esc="att.check_in.strftime('%d')"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fclk-recent-info">
|
||||
<div class="fclk-recent-location">
|
||||
<t t-esc="att.x_fclk_location_id.name or 'Unknown'"/>
|
||||
</div>
|
||||
<div class="fclk-recent-times">
|
||||
<t t-esc="att.check_in.strftime('%I:%M %p')"/>
|
||||
- <t t-esc="att.check_out.strftime('%I:%M %p') if att.check_out else '--'"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fclk-recent-hours">
|
||||
<t t-esc="'%.1f' % (att.x_fclk_net_hours or 0)"/>h
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="not recent_attendances">
|
||||
<div class="fclk-recent-empty">
|
||||
No recent activity
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Bar -->
|
||||
<div class="fclk-nav-bar">
|
||||
<a href="/my/clock" class="fclk-nav-item fclk-nav-active">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<polyline points="12 6 12 12 16 14"/>
|
||||
</svg>
|
||||
<span>Clock</span>
|
||||
</a>
|
||||
<a href="/my/clock/timesheets" class="fclk-nav-item">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/>
|
||||
<line x1="16" y1="2" x2="16" y2="6"/>
|
||||
<line x1="8" y1="2" x2="8" y2="6"/>
|
||||
<line x1="3" y1="10" x2="21" y2="10"/>
|
||||
</svg>
|
||||
<span>Timesheets</span>
|
||||
</a>
|
||||
<a href="/my/clock/reports" class="fclk-nav-item">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
<line x1="16" y1="13" x2="8" y2="13"/>
|
||||
<line x1="16" y1="17" x2="8" y2="17"/>
|
||||
</svg>
|
||||
<span>Reports</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Location Picker Modal -->
|
||||
<div class="fclk-modal" id="fclk-location-modal" style="display:none;">
|
||||
<div class="fclk-modal-backdrop" onclick="document.getElementById('fclk-location-modal').style.display='none'"></div>
|
||||
<div class="fclk-modal-content">
|
||||
<h3>Select Location</h3>
|
||||
<div class="fclk-modal-list">
|
||||
<t t-foreach="locations" t-as="loc">
|
||||
<div class="fclk-modal-item"
|
||||
t-att-data-id="loc.id"
|
||||
t-att-data-name="loc.name"
|
||||
t-att-data-address="loc.address or ''"
|
||||
t-att-data-lat="loc.latitude"
|
||||
t-att-data-lng="loc.longitude"
|
||||
t-att-data-radius="loc.radius">
|
||||
<div class="fclk-modal-item-icon">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#10B981" stroke-width="2">
|
||||
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/>
|
||||
<circle cx="12" cy="10" r="3"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fclk-modal-item-name"><t t-esc="loc.name"/></div>
|
||||
<div class="fclk-modal-item-addr"><t t-esc="loc.address or 'No address'"/></div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notification Toast -->
|
||||
<div class="fclk-toast" id="fclk-toast" style="display:none;">
|
||||
<div class="fclk-toast-icon" id="fclk-toast-icon"></div>
|
||||
<div class="fclk-toast-msg" id="fclk-toast-msg"></div>
|
||||
</div>
|
||||
|
||||
<!-- GPS Loading Overlay -->
|
||||
<div class="fclk-gps-overlay" id="fclk-gps-overlay" style="display:none;">
|
||||
<div class="fclk-gps-spinner"></div>
|
||||
<div class="fclk-gps-text">Verifying your location...</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Hidden location data for JS -->
|
||||
<script type="application/json" id="fclk-locations-data">
|
||||
<t t-out="locations_json"/>
|
||||
</script>
|
||||
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user