Add Timeclock settings tab in Company Settings with multi-kiosk support

Settings tab (Company Settings > Timeclock):
- Enable/disable timeclock toggle (hides nav link and attendance report when off)
- Allow multiple clock-ins per day toggle
- Auto clock-out after X hours (auto-closes forgotten open entries on next punch)
- Kiosk devices table: lists activated tablets with name, activated date, last seen;
  Deactivate button removes that device's access immediately

Multi-kiosk support (replaces single TimeclockKioskToken on Company):
- New TimeclockKioskDevice entity (one row per tablet, unique token, DeviceName, LastSeenAt)
- KioskActivate GET shows a form for optional device name before activating
- KioskDeactivate POST accepts device ID, deletes specific row (not all devices)
- Kiosk validation (Kiosk, KioskEmployees, KioskPunch) queries device table with
  ignoreQueryFilters since no user is logged in on kiosk requests
- LastSeenAt updated on each Kiosk page load

Enforcement:
- ClockIn and KioskPunch both auto-close stale entries if AutoClockOutHours is set
- ClockIn and KioskPunch both block second same-day punch if AllowMultiplePunches=false
- TimeclockEnabled=false hides nav link (SubscriptionMiddleware sets Items key) and
  returns Forbid on kiosk punch
- Migration: AddTimeclockSettings (adds 3 columns to Companies, new TimeclockKioskDevices table)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 00:12:46 -04:00
parent e124fd5c8b
commit 97745f9a65
12 changed files with 479 additions and 38 deletions
@@ -44,6 +44,20 @@ namespace PowderCoating.Application.DTOs.Company
/// <summary>True when the company has an accepted agreement for the current SmsTermsVersion.</summary>
public bool HasCurrentSmsAgreement { get; set; }
public string SmsTermsVersion { get; set; } = string.Empty;
// Timeclock settings
public bool TimeclockEnabled { get; set; }
public bool TimeclockAllowMultiplePunchesPerDay { get; set; }
public int? TimeclockAutoClockOutHours { get; set; }
}
/// <summary>DTO for updating company-level timeclock settings from the Settings tab.</summary>
public class UpdateTimeclockSettingsDto
{
public bool TimeclockEnabled { get; set; }
public bool TimeclockAllowMultiplePunchesPerDay { get; set; }
[Range(1, 24, ErrorMessage = "Auto clock-out must be between 1 and 24 hours.")]
public int? TimeclockAutoClockOutHours { get; set; }
}
/// <summary>