Implement comprehensive mobile experience framework for entire application
This commit is contained in:
48
.claude/agents/k8s-deployment-architect.md
Normal file
48
.claude/agents/k8s-deployment-architect.md
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
name: k8s-deployment-architect
|
||||
description: Use this agent when you need to design, review, or optimize Kubernetes deployments with a focus on high availability, maintainability, and GitOps automation. Examples: <example>Context: User is working on containerizing their MotoVaultPro application for Kubernetes deployment. user: 'I need to create Kubernetes manifests for my ASP.NET Core application that can be deployed with FluxCD' assistant: 'I'll use the k8s-deployment-architect agent to design production-ready Kubernetes manifests with proper high availability patterns and FluxCD integration.' <commentary>Since the user needs Kubernetes deployment architecture, use the k8s-deployment-architect agent to create maintainable, HA-focused manifests.</commentary></example> <example>Context: User wants to review existing Kubernetes configurations for best practices. user: 'Can you review my deployment.yaml and suggest improvements for better availability and maintainability?' assistant: 'Let me use the k8s-deployment-architect agent to analyze your Kubernetes configuration and provide recommendations for high availability and maintainability improvements.' <commentary>The user is asking for review of Kubernetes configurations, which is exactly what the k8s-deployment-architect agent specializes in.</commentary></example>
|
||||
color: purple
|
||||
---
|
||||
|
||||
You are an expert Kubernetes architect specializing in highly available, production-ready deployments with GitOps automation. Your expertise encompasses container orchestration, high availability patterns, and maintainable infrastructure-as-code practices.
|
||||
|
||||
Your primary responsibilities:
|
||||
|
||||
**High Availability Design:**
|
||||
- Design multi-replica deployments with proper anti-affinity rules
|
||||
- Implement health checks, readiness probes, and liveness probes
|
||||
- Configure resource limits, requests, and horizontal pod autoscaling
|
||||
- Design for graceful shutdowns and zero-downtime deployments
|
||||
- Implement circuit breakers and retry mechanisms where appropriate
|
||||
|
||||
**Maintainability Focus:**
|
||||
- Create clear, well-documented YAML manifests with extensive comments
|
||||
- Use consistent naming conventions and labeling strategies
|
||||
- Organize configurations into logical, modular files
|
||||
- Implement configuration management through ConfigMaps and Secrets
|
||||
- Design for easy debugging and troubleshooting by new team members
|
||||
|
||||
**FluxCD Integration:**
|
||||
- Structure manifests for GitOps workflows with proper directory organization
|
||||
- Implement Kustomization files for environment-specific configurations
|
||||
- Design for automated deployments with proper dependency management
|
||||
- Include monitoring and alerting configurations for deployment health
|
||||
- Ensure configurations support rollback and canary deployment strategies
|
||||
|
||||
**Code Quality Standards:**
|
||||
- Follow Kubernetes best practices and security guidelines
|
||||
- Implement proper RBAC and security contexts
|
||||
- Use semantic versioning for container images
|
||||
- Include comprehensive documentation within manifests
|
||||
- Design for testability and validation
|
||||
|
||||
**Onboarding Considerations:**
|
||||
- Create README files explaining the deployment architecture
|
||||
- Include troubleshooting guides and common operational procedures
|
||||
- Document environment-specific configurations and requirements
|
||||
- Provide clear examples and templates for common tasks
|
||||
- Implement logging and monitoring that aids in understanding system behavior
|
||||
|
||||
When reviewing existing configurations, provide specific, actionable recommendations with explanations of why each change improves availability or maintainability. When creating new configurations, start with the overall architecture and then provide detailed, production-ready manifests.
|
||||
|
||||
Always consider the operational burden on new team members and design solutions that are self-documenting and follow established patterns. Prioritize reliability and clarity over complexity.
|
||||
1
.github/workflows/build-and-push-image.yml
vendored
1
.github/workflows/build-and-push-image.yml
vendored
@@ -38,7 +38,6 @@ jobs:
|
||||
ericgullickson/motovaultpro
|
||||
ghcr.io/ericgullickson/motovaultpro
|
||||
tags: |
|
||||
type=edge,branch=main
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
type=ref,event=tag
|
||||
|
||||
|
||||
@@ -24,9 +24,9 @@ namespace MotoVaultPro.Helper
|
||||
public const string GenericErrorMessage = "An error occurred, please try again later";
|
||||
public const string ReminderEmailTemplate = "defaults/reminderemailtemplate.txt";
|
||||
public const string DefaultAllowedFileExtensions = ".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx";
|
||||
public const string SponsorsPath = "https://fbtech.github.io/fbtech/sponsors.json";
|
||||
public const string TranslationPath = "https://fbtech.github.io/motovaultpro_translations";
|
||||
public const string ReleasePath = "https://api.github.com/repos/fbtech/motovaultpro/releases/latest";
|
||||
public const string SponsorsPath = "https://ericgullickson.github.io/ericgullickson/sponsors.json";
|
||||
public const string TranslationPath = "https://ericgullickson.github.io/motovaultpro_translations";
|
||||
public const string ReleasePath = "https://api.github.com/repos/ericgullickson/motovaultpro/releases/latest";
|
||||
public const string TranslationDirectoryPath = $"{TranslationPath}/directory.json";
|
||||
public const string ReportNote = "Report generated by MotoVaultPro, a Free and Open Source Vehicle Maintenance Tracker - MotoVaultPro.com";
|
||||
public static string GetTitleCaseReminderUrgency(ReminderUrgency input)
|
||||
@@ -315,7 +315,7 @@ namespace MotoVaultPro.Helper
|
||||
Console.WriteLine($"MotoVaultPro {VersionNumber}");
|
||||
Console.WriteLine("Website: https://motovaultpro.com");
|
||||
Console.WriteLine("Documentation: https://docs.motovaultpro.com");
|
||||
Console.WriteLine("GitHub: https://github.com/fbtech/motovaultpro");
|
||||
Console.WriteLine("GitHub: https://github.com/ericgullickson/motovaultpro");
|
||||
var mailConfig = config.GetSection("MailConfig").Get<MailConfig>();
|
||||
if (mailConfig != null && !string.IsNullOrWhiteSpace(mailConfig.EmailServer))
|
||||
{
|
||||
|
||||
185
MOBILE.md
Normal file
185
MOBILE.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# Mobile Experience Improvement Plan for Add Fuel Record Screen
|
||||
|
||||
## Analysis Summary
|
||||
|
||||
The current add fuel record screen has significant mobile UX issues that create pain points for users on mobile devices. The interface feels like a shrunken desktop version rather than a mobile-first experience.
|
||||
|
||||
## Critical Mobile UX Issues Identified
|
||||
|
||||
### 1. Modal Size and Viewport Problems
|
||||
- Uses Bootstrap's default modal sizing without mobile optimization
|
||||
- No mobile-specific modal sizing classes or responsive adjustments
|
||||
- **File Location**: `/Views/Vehicle/Gas/_GasModal.cshtml`
|
||||
|
||||
### 2. Touch Target Size Issues
|
||||
- Small "+" button for odometer increment (44px minimum not met)
|
||||
- Small close button in header
|
||||
- Form switch toggles too small for reliable touch interaction
|
||||
- **File Locations**:
|
||||
- `/Views/Vehicle/Gas/_GasModal.cshtml` (lines 69, 99, 51, 48, 106, 110)
|
||||
|
||||
### 3. Dense Two-Column Layout Problems
|
||||
- Advanced mode uses `col-md-6` layout creating cramped display
|
||||
- Fields become too narrow for comfortable text input
|
||||
- Second column with file upload becomes nearly unusable
|
||||
- **File Location**: `/Views/Vehicle/Gas/_GasModal.cshtml` (lines 59, 139)
|
||||
|
||||
### 4. Complex Header Layout on Mobile
|
||||
- Modal header contains multiple elements in cramped flex layout
|
||||
- Toggle labels may wrap or get cut off
|
||||
- Mode switch becomes hard to understand and use
|
||||
- **File Location**: `/Views/Vehicle/Gas/_GasModal.cshtml` (lines 44-53)
|
||||
|
||||
### 5. Input Field Accessibility Issues
|
||||
- Decimal inputs with custom key interceptors interfere with mobile keyboards
|
||||
- Multi-select dropdown for tags difficult on mobile
|
||||
- File upload interface unusable in narrow mobile view
|
||||
- **File Locations**:
|
||||
- `/Views/Vehicle/Gas/_GasModal.cshtml` (lines 74, 103, 117, 127, 130-135)
|
||||
- `/wwwroot/js/gasrecord.js`
|
||||
|
||||
### 6. Modal Footer Button Layout
|
||||
- Multiple buttons including conditional "Delete" button create touch conflicts
|
||||
- Risk of accidental deletion or difficulty reaching primary action
|
||||
- **File Location**: `/Views/Vehicle/Gas/_GasModal.cshtml` (line 155)
|
||||
|
||||
### 7. Form Mode Switching UX
|
||||
- Simple/Advanced mode toggle jarring on mobile
|
||||
- Content suddenly appears/disappears
|
||||
- Users might not understand mode switching capability
|
||||
- **File Location**: `/wwwroot/js/gasrecord.js` (lines 509-536)
|
||||
|
||||
### 8. Keyboard and Input Mode Issues
|
||||
- Mixed input types with custom JavaScript key handlers
|
||||
- Mobile keyboards may not behave predictably
|
||||
- **File Locations**:
|
||||
- `/Views/Vehicle/Gas/_GasModal.cshtml`
|
||||
- `/wwwroot/js/gasrecord.js`
|
||||
|
||||
### 9. Date Picker Mobile Issues
|
||||
- Bootstrap datepicker doesn't provide optimal mobile experience
|
||||
- Native mobile date pickers would be better
|
||||
- **File Location**: `/wwwroot/js/gasrecord.js` (lines 6, 29)
|
||||
|
||||
### 10. No Progressive Enhancement for Mobile
|
||||
- No mobile-specific CSS classes or touch-friendly spacing
|
||||
- No mobile-optimized layouts
|
||||
- **File Locations**:
|
||||
- `/wwwroot/css/site.css`
|
||||
- `/Views/Vehicle/Gas/_GasModal.cshtml`
|
||||
|
||||
## Mobile Experience Improvement Plan
|
||||
|
||||
### Priority 1: Critical Mobile UX Fixes
|
||||
|
||||
#### 1. Mobile-First Modal Design
|
||||
- Implement full-screen modal on mobile devices
|
||||
- Add slide-up animation for native app feel
|
||||
- Create mobile-specific modal header with simplified layout
|
||||
- **Files to Modify**:
|
||||
- `/Views/Vehicle/Gas/_GasModal.cshtml`
|
||||
- `/wwwroot/css/site.css`
|
||||
- `/wwwroot/js/gasrecord.js`
|
||||
|
||||
#### 2. Touch Target Optimization
|
||||
- Increase all interactive elements to minimum 44px
|
||||
- Add larger padding around buttons and form controls
|
||||
- Implement touch-friendly spacing between elements
|
||||
- **Files to Modify**:
|
||||
- `/Views/Vehicle/Gas/_GasModal.cshtml`
|
||||
- `/wwwroot/css/site.css`
|
||||
|
||||
#### 3. Single-Column Mobile Layout
|
||||
- Force single-column layout on mobile regardless of mode
|
||||
- Stack all form fields vertically with proper spacing
|
||||
- Move file upload and notes to dedicated sections
|
||||
- **Files to Modify**:
|
||||
- `/Views/Vehicle/Gas/_GasModal.cshtml`
|
||||
- `/wwwroot/css/site.css`
|
||||
|
||||
### Priority 2: Input and Interaction Improvements
|
||||
|
||||
#### 4. Mobile-Optimized Inputs
|
||||
- Replace Bootstrap datepicker with native HTML5 date input on mobile
|
||||
- Simplify tag selection with mobile-friendly chip input
|
||||
- Improve number input keyboards with proper `inputmode` attributes
|
||||
- **Files to Modify**:
|
||||
- `/Views/Vehicle/Gas/_GasModal.cshtml`
|
||||
- `/wwwroot/js/gasrecord.js`
|
||||
|
||||
#### 5. Form Mode Simplification
|
||||
- Default to Simple mode on mobile
|
||||
- Make mode toggle more prominent and clear
|
||||
- Add smooth transitions between modes
|
||||
- **Files to Modify**:
|
||||
- `/Views/Vehicle/Gas/_GasModal.cshtml`
|
||||
- `/wwwroot/js/gasrecord.js`
|
||||
- `/Controllers/Vehicle/GasController.cs`
|
||||
|
||||
### Priority 3: Enhanced Mobile Features
|
||||
|
||||
#### 6. Bottom Sheet Pattern
|
||||
- Implement native-style bottom sheet for mobile
|
||||
- Add swipe-to-dismiss gesture
|
||||
- Include pull handle for better UX
|
||||
- **Files to Modify**:
|
||||
- `/Views/Vehicle/Gas/_GasModal.cshtml`
|
||||
- `/wwwroot/css/site.css`
|
||||
- `/wwwroot/js/gasrecord.js`
|
||||
|
||||
#### 7. Mobile-Specific CSS Improvements
|
||||
- Add mobile breakpoint styles
|
||||
- Implement proper touch feedback
|
||||
- Optimize form field sizing for mobile keyboards
|
||||
- **Files to Modify**:
|
||||
- `/wwwroot/css/site.css`
|
||||
|
||||
#### 8. Progressive Enhancement
|
||||
- Add mobile detection for conditional features
|
||||
- Implement haptic feedback where supported
|
||||
- Add mobile-specific validation styling
|
||||
- **Files to Modify**:
|
||||
- `/wwwroot/js/gasrecord.js`
|
||||
- `/wwwroot/js/shared.js`
|
||||
- `/Views/Shared/_Layout.cshtml`
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### Phase 1: Modal and Layout Fixes (Priority 1 items)
|
||||
- Focus on making the most impactful changes first
|
||||
- Ensure mobile modal feels native and intuitive
|
||||
- Implement proper touch targets and single-column layout
|
||||
|
||||
### Phase 2: Input Optimizations (Priority 2 items)
|
||||
- Optimize form inputs for mobile interaction
|
||||
- Simplify complex form elements
|
||||
- Improve mode switching experience
|
||||
|
||||
### Phase 3: Advanced Mobile Features (Priority 3 items)
|
||||
- Add sophisticated mobile interaction patterns
|
||||
- Implement progressive enhancement
|
||||
- Add mobile-specific features and feedback
|
||||
|
||||
## Key Files for Mobile Improvements
|
||||
|
||||
### Primary Files:
|
||||
- `/Views/Vehicle/Gas/_GasModal.cshtml` - Main modal template
|
||||
- `/wwwroot/js/gasrecord.js` - Modal behavior and form handling
|
||||
- `/wwwroot/css/site.css` - Styling and responsive design
|
||||
|
||||
### Supporting Files:
|
||||
- `/Controllers/Vehicle/GasController.cs` - Server-side logic
|
||||
- `/Views/Shared/_Layout.cshtml` - Global mobile configuration
|
||||
- `/wwwroot/js/shared.js` - Shared JavaScript utilities
|
||||
|
||||
## Success Metrics
|
||||
|
||||
- Touch target compliance (minimum 44px)
|
||||
- Single-column layout on mobile breakpoints
|
||||
- Native mobile input patterns
|
||||
- Improved task completion rates on mobile
|
||||
- Reduced user friction and abandonment
|
||||
|
||||
## Notes
|
||||
|
||||
This plan maintains existing functionality while transforming the mobile experience from a desktop-centric interface to a mobile-first, touch-optimized experience that feels native and intuitive on mobile devices.
|
||||
@@ -15,7 +15,7 @@
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-12">
|
||||
<div class="col-md-6 col-12 mobile-single-column">
|
||||
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
|
||||
<label for="collisionRecordDate">@translator.Translate(userLanguage, "Date")</label>
|
||||
<div class="input-group">
|
||||
@@ -54,7 +54,7 @@
|
||||
</select>
|
||||
@await Html.PartialAsync("_ExtraField", Model.ExtraFields)
|
||||
</div>
|
||||
<div class="col-md-6 col-12">
|
||||
<div class="col-md-6 col-12 mobile-single-column">
|
||||
<label for="collisionRecordNotes">@translator.Translate(userLanguage, "Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
|
||||
<textarea id="collisionRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
|
||||
@if (isNew)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-12" id="reminderOptions">
|
||||
<div class="col-md-6 col-12 mobile-single-column" id="reminderOptions">
|
||||
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
|
||||
<label for="reminderDescription">@translator.Translate(userLanguage,"Description")</label>
|
||||
<input type="text" id="reminderDescription" class="form-control" placeholder="@translator.Translate(userLanguage,"Reminder Description")" value="@Model.Description">
|
||||
@@ -54,7 +54,7 @@
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 col-12">
|
||||
<div class="col-md-6 col-12 mobile-single-column">
|
||||
<label for="reminderNotes">@translator.Translate(userLanguage,"Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
|
||||
<textarea id="reminderNotes" class="form-control" rows="5">@Model.Notes</textarea>
|
||||
<div class="form-check form-switch">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-12">
|
||||
<div class="col-md-6 col-12 mobile-single-column">
|
||||
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
|
||||
<label for="serviceRecordDate">@translator.Translate(userLanguage,"Date")</label>
|
||||
<div class="input-group">
|
||||
@@ -54,7 +54,7 @@
|
||||
</select>
|
||||
@await Html.PartialAsync("_ExtraField", Model.ExtraFields)
|
||||
</div>
|
||||
<div class="col-md-6 col-12">
|
||||
<div class="col-md-6 col-12 mobile-single-column">
|
||||
<label for="serviceRecordNotes">@translator.Translate(userLanguage,"Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
|
||||
<textarea id="serviceRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
|
||||
@if (isNew)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-12">
|
||||
<div class="col-md-6 col-12 mobile-single-column">
|
||||
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
|
||||
<label for="taxRecordDate">@translator.Translate(userLanguage,"Date")</label>
|
||||
<div class="input-group">
|
||||
@@ -43,7 +43,7 @@
|
||||
</select>
|
||||
@await Html.PartialAsync("_ExtraField", Model.ExtraFields)
|
||||
</div>
|
||||
<div class="col-md-6 col-12">
|
||||
<div class="col-md-6 col-12 mobile-single-column">
|
||||
<label for="taxRecordNotes">@translator.Translate(userLanguage,"Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
|
||||
<textarea id="taxRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
|
||||
<div class="form-check form-switch">
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-12">
|
||||
<div class="col-md-6 col-12 mobile-single-column">
|
||||
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
|
||||
<label for="upgradeRecordDate">@translator.Translate(userLanguage, "Date")</label>
|
||||
<div class="input-group">
|
||||
@@ -54,7 +54,7 @@
|
||||
</select>
|
||||
@await Html.PartialAsync("_ExtraField", Model.ExtraFields)
|
||||
</div>
|
||||
<div class="col-md-6 col-12">
|
||||
<div class="col-md-6 col-12 mobile-single-column">
|
||||
<label for="upgradeRecordNotes">@translator.Translate(userLanguage, "Notes(optional)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
|
||||
<textarea id="upgradeRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
|
||||
@if (isNew)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<form class="form-inline">
|
||||
<div class="form-group">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="col-12 col-md-6 mobile-single-column">
|
||||
<label for="inputYear">@translator.Translate(userLanguage, "Year")</label>
|
||||
<input type="number" inputmode="numeric" id="inputYear" class="form-control" placeholder="@translator.Translate(userLanguage, "Year(must be after 1900)")" value="@(isNew ? "" : Model.Year)">
|
||||
<label for="inputMake">@translator.Translate(userLanguage, "Make")</label>
|
||||
@@ -46,7 +46,7 @@
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="col-12 col-md-6 mobile-single-column">
|
||||
<label for="inputFuelType">@translator.Translate(userLanguage, "Fuel Type")</label>
|
||||
<select class="form-select" id="inputFuelType")>
|
||||
<!option value="Gasoline" @(!Model.IsDiesel && !Model.IsElectric ? "selected" : "")>@translator.Translate(userLanguage, "Gasoline")</!option>
|
||||
|
||||
@@ -378,6 +378,177 @@ html {
|
||||
.is-valid:focus {
|
||||
box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25) !important;
|
||||
}
|
||||
|
||||
/* iPhone-specific optimizations for high DPI displays */
|
||||
.modal-header .modal-title {
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.3;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* Optimize for very narrow screens (iPhone portrait) */
|
||||
.modal-body .form-group {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.modal-body .form-group label {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.5rem;
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Enhanced input styling for high DPI */
|
||||
.form-control,
|
||||
.form-select {
|
||||
border-width: 1.5px;
|
||||
border-radius: 8px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Improved button styling for high DPI touch */
|
||||
.btn {
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.025em;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* Enhanced modal animations for smooth feel */
|
||||
.modal.fade .modal-dialog {
|
||||
transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
/* Better spacing for very narrow viewports */
|
||||
.input-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.input-group-text {
|
||||
border-width: 1.5px;
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
.input-group .form-control {
|
||||
border-radius: 8px 0 0 8px;
|
||||
}
|
||||
|
||||
/* Optimize checkbox and radio sizing for high DPI */
|
||||
.form-check-input {
|
||||
margin-top: 0.125rem;
|
||||
border-width: 1.5px;
|
||||
}
|
||||
|
||||
.form-check-label {
|
||||
font-size: 0.9rem;
|
||||
padding-left: 0.75rem;
|
||||
}
|
||||
|
||||
/* Enhanced focus rings for high DPI */
|
||||
.form-control:focus,
|
||||
.form-select:focus,
|
||||
.btn:focus {
|
||||
box-shadow: 0 0 0 3px rgba(13, 110, 253, 0.15);
|
||||
border-color: #86b7fe;
|
||||
}
|
||||
|
||||
/* Better modal pull handle for high DPI */
|
||||
.modal-content::before {
|
||||
width: 48px;
|
||||
height: 5px;
|
||||
border-radius: 3px;
|
||||
background-color: #adb5bd;
|
||||
top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ultra-narrow portrait optimization (iPhone-specific) */
|
||||
@media (max-width: 280px) {
|
||||
.modal-header {
|
||||
padding: 0.75rem;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.modal-header .btn-close {
|
||||
position: absolute;
|
||||
top: 0.75rem;
|
||||
right: 0.75rem;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.modal-header .modal-title {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
padding-right: 50px; /* Account for close button */
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 0.75rem;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
/* Ultra-compact form styling */
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 0.375rem;
|
||||
}
|
||||
|
||||
.form-control,
|
||||
.form-select {
|
||||
padding: 0.625rem 0.75rem;
|
||||
font-size: 15px; /* Prevent zoom on very narrow iPhones */
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 0.625rem 1rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Compact input groups */
|
||||
.input-group-text {
|
||||
padding: 0.625rem 0.75rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Smaller modal content spacing */
|
||||
.modal-content::before {
|
||||
width: 40px;
|
||||
height: 4px;
|
||||
top: 8px;
|
||||
}
|
||||
|
||||
/* Optimize for one-handed use */
|
||||
.modal-footer .btn {
|
||||
min-height: 48px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* Better tag input styling for narrow screens */
|
||||
.mobile-tag-input .bootstrap-tagsinput {
|
||||
min-height: 40px;
|
||||
padding: 6px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.mobile-tag-input .tag {
|
||||
font-size: 0.8rem;
|
||||
padding: 3px 6px;
|
||||
margin: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.recordSticker {
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
// Initialize collision record modal for mobile (using shared mobile framework)
|
||||
function initializeCollisionRecordMobile() {
|
||||
initMobileModal({
|
||||
modalId: '#collisionRecordModal',
|
||||
dateInputId: '#collisionRecordDate',
|
||||
tagSelectorId: '#collisionRecordTag'
|
||||
});
|
||||
|
||||
// Handle desktop initialization
|
||||
if (!isMobileDevice()) {
|
||||
initDatePicker($('#collisionRecordDate'));
|
||||
initTagSelector($("#collisionRecordTag"));
|
||||
}
|
||||
}
|
||||
|
||||
function showAddCollisionRecordModal() {
|
||||
$.get('/Vehicle/GetAddCollisionRecordPartialView', function (data) {
|
||||
if (data) {
|
||||
$("#collisionRecordModalContent").html(data);
|
||||
//initiate datepicker
|
||||
initDatePicker($('#collisionRecordDate'));
|
||||
initTagSelector($("#collisionRecordTag"));
|
||||
|
||||
// Initialize mobile experience using shared framework
|
||||
initializeCollisionRecordMobile();
|
||||
|
||||
$('#collisionRecordModal').modal('show');
|
||||
}
|
||||
});
|
||||
@@ -25,9 +41,8 @@ function showEditCollisionRecordModal(collisionRecordId, nocache) {
|
||||
$.get(`/Vehicle/GetCollisionRecordForEditById?collisionRecordId=${collisionRecordId}`, function (data) {
|
||||
if (data) {
|
||||
$("#collisionRecordModalContent").html(data);
|
||||
//initiate datepicker
|
||||
initDatePicker($('#collisionRecordDate'));
|
||||
initTagSelector($("#collisionRecordTag"));
|
||||
// Initialize mobile experience using shared framework
|
||||
initializeCollisionRecordMobile();
|
||||
$('#collisionRecordModal').modal('show');
|
||||
bindModalInputChanges('collisionRecordModal');
|
||||
$('#collisionRecordModal').off('shown.bs.modal').on('shown.bs.modal', function () {
|
||||
|
||||
@@ -1,11 +1,33 @@
|
||||
// Initialize vehicle modal for mobile (using shared mobile framework)
|
||||
function initializeVehicleMobile() {
|
||||
// Vehicle modal has multiple date inputs, handle them individually
|
||||
if (isMobileDevice()) {
|
||||
// Convert date inputs to native HTML5 on mobile
|
||||
$('#inputPurchaseDate').attr('type', 'date').removeClass('datepicker');
|
||||
$('#inputSoldDate').attr('type', 'date').removeClass('datepicker');
|
||||
|
||||
// Initialize mobile tag selector
|
||||
initMobileTagSelector($("#inputTag"));
|
||||
|
||||
// Initialize swipe to dismiss
|
||||
initSwipeToDismiss('#addVehicleModal');
|
||||
} else {
|
||||
// Desktop initialization
|
||||
initTagSelector($("#inputTag"));
|
||||
initDatePicker($('#inputPurchaseDate'));
|
||||
initDatePicker($('#inputSoldDate'));
|
||||
}
|
||||
}
|
||||
|
||||
function showAddVehicleModal() {
|
||||
uploadedFile = "";
|
||||
$.get('/Vehicle/AddVehiclePartialView', function (data) {
|
||||
if (data) {
|
||||
$("#addVehicleModalContent").html(data);
|
||||
initTagSelector($("#inputTag"));
|
||||
initDatePicker($('#inputPurchaseDate'));
|
||||
initDatePicker($('#inputSoldDate'));
|
||||
|
||||
// Initialize mobile experience using shared framework
|
||||
initializeVehicleMobile();
|
||||
|
||||
$('#addVehicleModal').modal('show');
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,133 +1,29 @@
|
||||
// Mobile detection utility
|
||||
function isMobileDevice() {
|
||||
return window.matchMedia("(max-width: 768px)").matches ||
|
||||
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||
}
|
||||
// Initialize gas record modal for mobile (using shared mobile framework)
|
||||
function initializeGasRecordMobile() {
|
||||
initMobileModal({
|
||||
modalId: '#gasRecordModal',
|
||||
dateInputId: '#gasRecordDate',
|
||||
tagSelectorId: '#gasRecordTag',
|
||||
modeToggleId: '#fuelEntryModeToggle',
|
||||
simpleModeDefault: true
|
||||
});
|
||||
|
||||
// Initialize form inputs based on device type
|
||||
function initializeFormInputs() {
|
||||
if (isMobileDevice()) {
|
||||
// Convert date input to native HTML5 on mobile
|
||||
var dateInput = $('#gasRecordDate');
|
||||
if (dateInput.length) {
|
||||
dateInput.attr('type', 'date');
|
||||
dateInput.removeClass('datepicker'); // Remove Bootstrap datepicker class
|
||||
}
|
||||
|
||||
// Default to simple mode on mobile
|
||||
var modeToggle = $('#fuelEntryModeToggle');
|
||||
if (modeToggle.length && !modeToggle.is(':checked')) {
|
||||
modeToggle.prop('checked', true);
|
||||
toggleFuelEntryMode(true);
|
||||
}
|
||||
|
||||
// Initialize mobile-friendly tag selector
|
||||
initMobileTagSelector($("#gasRecordTag"));
|
||||
} else {
|
||||
// Use Bootstrap datepicker on desktop
|
||||
// Handle desktop initialization
|
||||
if (!isMobileDevice()) {
|
||||
initDatePicker($('#gasRecordDate'));
|
||||
|
||||
// Use standard tag selector on desktop
|
||||
initTagSelector($("#gasRecordTag"));
|
||||
}
|
||||
}
|
||||
|
||||
// Mobile-optimized tag selector
|
||||
function initMobileTagSelector(input) {
|
||||
if (input.length) {
|
||||
// Initialize with mobile-friendly options
|
||||
input.tagsinput({
|
||||
maxTags: 5, // Limit tags on mobile
|
||||
trimValue: true,
|
||||
confirmKeys: [13, 44, 32], // Enter, comma, space
|
||||
focusClass: 'focus',
|
||||
freeInput: true
|
||||
});
|
||||
|
||||
// Add mobile-specific styling
|
||||
input.parent().addClass('mobile-tag-input');
|
||||
|
||||
// Increase touch target size for mobile
|
||||
input.parent().find('.bootstrap-tagsinput').css({
|
||||
'min-height': '44px',
|
||||
'padding': '8px',
|
||||
'font-size': '16px' // Prevent zoom on iOS
|
||||
});
|
||||
|
||||
// Style the input within tags
|
||||
input.parent().find('.bootstrap-tagsinput input').css({
|
||||
'font-size': '16px',
|
||||
'min-height': '30px'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Swipe to dismiss functionality for mobile modals
|
||||
function initSwipeToDismiss() {
|
||||
if (!isMobileDevice()) return;
|
||||
|
||||
var modal = $('#gasRecordModal');
|
||||
var modalContent = modal.find('.modal-content');
|
||||
var startY = 0;
|
||||
var currentY = 0;
|
||||
var isDragging = false;
|
||||
var threshold = 100; // Minimum swipe distance to dismiss
|
||||
|
||||
// Touch start
|
||||
modalContent.on('touchstart', function(e) {
|
||||
startY = e.originalEvent.touches[0].clientY;
|
||||
isDragging = true;
|
||||
modalContent.css('transition', 'none');
|
||||
});
|
||||
|
||||
// Touch move
|
||||
modalContent.on('touchmove', function(e) {
|
||||
if (!isDragging) return;
|
||||
|
||||
currentY = e.originalEvent.touches[0].clientY;
|
||||
var deltaY = currentY - startY;
|
||||
|
||||
// Only allow downward swipes
|
||||
if (deltaY > 0) {
|
||||
modalContent.css('transform', `translateY(${deltaY}px)`);
|
||||
}
|
||||
});
|
||||
|
||||
// Touch end
|
||||
modalContent.on('touchend', function(e) {
|
||||
if (!isDragging) return;
|
||||
|
||||
isDragging = false;
|
||||
var deltaY = currentY - startY;
|
||||
|
||||
modalContent.css('transition', 'transform 0.3s ease-out');
|
||||
|
||||
if (deltaY > threshold) {
|
||||
// Dismiss modal
|
||||
modalContent.css('transform', 'translateY(100%)');
|
||||
setTimeout(function() {
|
||||
hideAddGasRecordModal();
|
||||
modalContent.css('transform', '');
|
||||
}, 300);
|
||||
} else {
|
||||
// Snap back
|
||||
modalContent.css('transform', '');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showAddGasRecordModal() {
|
||||
$.get(`/Vehicle/GetAddGasRecordPartialView?vehicleId=${GetVehicleId().vehicleId}`, function (data) {
|
||||
if (data) {
|
||||
$("#gasRecordModalContent").html(data);
|
||||
|
||||
// Initialize inputs based on device type
|
||||
initializeFormInputs();
|
||||
// Initialize mobile experience using shared framework
|
||||
initializeGasRecordMobile();
|
||||
|
||||
$('#gasRecordModal').modal('show');
|
||||
|
||||
// Initialize swipe to dismiss for mobile
|
||||
initSwipeToDismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -148,15 +44,12 @@ function showEditGasRecordModal(gasRecordId, nocache) {
|
||||
if (data) {
|
||||
$("#gasRecordModalContent").html(data);
|
||||
|
||||
// Initialize inputs based on device type
|
||||
initializeFormInputs();
|
||||
// Initialize mobile experience using shared framework
|
||||
initializeGasRecordMobile();
|
||||
|
||||
$('#gasRecordModal').modal('show');
|
||||
bindModalInputChanges('gasRecordModal');
|
||||
|
||||
// Initialize swipe to dismiss for mobile
|
||||
initSwipeToDismiss();
|
||||
|
||||
$('#gasRecordModal').off('shown.bs.modal').on('shown.bs.modal', function () {
|
||||
if (getGlobalConfig().useMarkDown) {
|
||||
toggleMarkDownOverlay("gasRecordNotes");
|
||||
@@ -569,13 +462,10 @@ function editMultipleGasRecords(ids) {
|
||||
if (data) {
|
||||
$("#gasRecordModalContent").html(data);
|
||||
|
||||
// Initialize inputs based on device type
|
||||
initializeFormInputs();
|
||||
// Initialize mobile experience using shared framework
|
||||
initializeGasRecordMobile();
|
||||
|
||||
$('#gasRecordModal').modal('show');
|
||||
|
||||
// Initialize swipe to dismiss for mobile
|
||||
initSwipeToDismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
// Initialize reminder record modal for mobile (using shared mobile framework)
|
||||
function initializeReminderRecordMobile() {
|
||||
initMobileModal({
|
||||
modalId: '#reminderRecordModal',
|
||||
dateInputId: '#reminderDate',
|
||||
tagSelectorId: '#reminderRecordTag'
|
||||
});
|
||||
|
||||
// Handle desktop initialization
|
||||
if (!isMobileDevice()) {
|
||||
initDatePicker($('#reminderDate'), true);
|
||||
initTagSelector($("#reminderRecordTag"));
|
||||
}
|
||||
}
|
||||
|
||||
function showEditReminderRecordModal(reminderId) {
|
||||
$.get(`/Vehicle/GetReminderRecordForEditById?reminderRecordId=${reminderId}`, function (data) {
|
||||
if (data) {
|
||||
$("#reminderRecordModalContent").html(data);
|
||||
initDatePicker($('#reminderDate'), true);
|
||||
initTagSelector($("#reminderRecordTag"));
|
||||
|
||||
// Initialize mobile experience using shared framework
|
||||
initializeReminderRecordMobile();
|
||||
|
||||
$("#reminderRecordModal").modal("show");
|
||||
$('#reminderRecordModal').off('shown.bs.modal').on('shown.bs.modal', function () {
|
||||
if (getGlobalConfig().useMarkDown) {
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
// Initialize service record modal for mobile (using shared mobile framework)
|
||||
function initializeServiceRecordMobile() {
|
||||
initMobileModal({
|
||||
modalId: '#serviceRecordModal',
|
||||
dateInputId: '#serviceRecordDate',
|
||||
tagSelectorId: '#serviceRecordTag'
|
||||
});
|
||||
|
||||
// Handle desktop initialization
|
||||
if (!isMobileDevice()) {
|
||||
initDatePicker($('#serviceRecordDate'));
|
||||
initTagSelector($("#serviceRecordTag"));
|
||||
}
|
||||
}
|
||||
|
||||
function showAddServiceRecordModal() {
|
||||
$.get('/Vehicle/GetAddServiceRecordPartialView', function (data) {
|
||||
if (data) {
|
||||
$("#serviceRecordModalContent").html(data);
|
||||
//initiate datepicker
|
||||
initDatePicker($('#serviceRecordDate'));
|
||||
initTagSelector($("#serviceRecordTag"));
|
||||
|
||||
// Initialize mobile experience using shared framework
|
||||
initializeServiceRecordMobile();
|
||||
|
||||
$('#serviceRecordModal').modal('show');
|
||||
}
|
||||
});
|
||||
@@ -25,9 +41,10 @@ function showEditServiceRecordModal(serviceRecordId, nocache) {
|
||||
$.get(`/Vehicle/GetServiceRecordForEditById?serviceRecordId=${serviceRecordId}`, function (data) {
|
||||
if (data) {
|
||||
$("#serviceRecordModalContent").html(data);
|
||||
//initiate datepicker
|
||||
initDatePicker($('#serviceRecordDate'));
|
||||
initTagSelector($("#serviceRecordTag"));
|
||||
|
||||
// Initialize mobile experience using shared framework
|
||||
initializeServiceRecordMobile();
|
||||
|
||||
$('#serviceRecordModal').modal('show');
|
||||
bindModalInputChanges('serviceRecordModal');
|
||||
$('#serviceRecordModal').off('shown.bs.modal').on('shown.bs.modal', function () {
|
||||
|
||||
@@ -375,6 +375,149 @@ function initTagSelector(input, noDataList) {
|
||||
input.tagsinput();
|
||||
}
|
||||
}
|
||||
|
||||
// ===== MOBILE EXPERIENCE FRAMEWORK =====
|
||||
|
||||
// Mobile detection utility
|
||||
function isMobileDevice() {
|
||||
return window.matchMedia("(max-width: 768px)").matches ||
|
||||
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||
}
|
||||
|
||||
// Mobile-optimized tag selector
|
||||
function initMobileTagSelector(input) {
|
||||
if (input.length) {
|
||||
// Initialize with mobile-friendly options
|
||||
input.tagsinput({
|
||||
maxTags: 5, // Limit tags on mobile
|
||||
trimValue: true,
|
||||
confirmKeys: [13, 44, 32], // Enter, comma, space
|
||||
focusClass: 'focus',
|
||||
freeInput: true
|
||||
});
|
||||
|
||||
// Add mobile-specific styling
|
||||
input.parent().addClass('mobile-tag-input');
|
||||
|
||||
// Increase touch target size for mobile
|
||||
input.parent().find('.bootstrap-tagsinput').css({
|
||||
'min-height': '44px',
|
||||
'padding': '8px',
|
||||
'font-size': '16px' // Prevent zoom on iOS
|
||||
});
|
||||
|
||||
// Style the input within tags
|
||||
input.parent().find('.bootstrap-tagsinput input').css({
|
||||
'font-size': '16px',
|
||||
'min-height': '30px'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Universal mobile modal initializer
|
||||
function initMobileModal(config) {
|
||||
if (!isMobileDevice()) return;
|
||||
|
||||
var modalId = config.modalId || '#modal';
|
||||
var dateInputId = config.dateInputId;
|
||||
var tagSelectorId = config.tagSelectorId;
|
||||
var modeToggleId = config.modeToggleId;
|
||||
var simpleModeDefault = config.simpleModeDefault || false;
|
||||
|
||||
// Convert date input to native HTML5 on mobile
|
||||
if (dateInputId) {
|
||||
var dateInput = $(dateInputId);
|
||||
if (dateInput.length) {
|
||||
dateInput.attr('type', 'date');
|
||||
dateInput.removeClass('datepicker'); // Remove Bootstrap datepicker class
|
||||
}
|
||||
}
|
||||
|
||||
// Default to simple mode on mobile if toggle exists
|
||||
if (modeToggleId && simpleModeDefault) {
|
||||
var modeToggle = $(modeToggleId);
|
||||
if (modeToggle.length && !modeToggle.is(':checked')) {
|
||||
modeToggle.prop('checked', true);
|
||||
// Try to call the mode toggle function if it exists
|
||||
if (typeof toggleFuelEntryMode === 'function') {
|
||||
toggleFuelEntryMode(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize mobile-friendly tag selector
|
||||
if (tagSelectorId) {
|
||||
initMobileTagSelector($(tagSelectorId));
|
||||
}
|
||||
|
||||
// Initialize swipe to dismiss
|
||||
initSwipeToDismiss(modalId);
|
||||
}
|
||||
|
||||
// Swipe to dismiss functionality for mobile modals
|
||||
function initSwipeToDismiss(modalSelector) {
|
||||
if (!isMobileDevice()) return;
|
||||
|
||||
modalSelector = modalSelector || '#modal';
|
||||
var modal = $(modalSelector);
|
||||
var modalContent = modal.find('.modal-content');
|
||||
var startY = 0;
|
||||
var currentY = 0;
|
||||
var isDragging = false;
|
||||
var threshold = 100; // Minimum swipe distance to dismiss
|
||||
|
||||
// Remove any existing touch handlers to avoid conflicts
|
||||
modalContent.off('touchstart touchmove touchend');
|
||||
|
||||
// Touch start
|
||||
modalContent.on('touchstart', function(e) {
|
||||
startY = e.originalEvent.touches[0].clientY;
|
||||
isDragging = true;
|
||||
modalContent.css('transition', 'none');
|
||||
});
|
||||
|
||||
// Touch move
|
||||
modalContent.on('touchmove', function(e) {
|
||||
if (!isDragging) return;
|
||||
|
||||
currentY = e.originalEvent.touches[0].clientY;
|
||||
var deltaY = currentY - startY;
|
||||
|
||||
// Only allow downward swipes
|
||||
if (deltaY > 0) {
|
||||
modalContent.css('transform', `translateY(${deltaY}px)`);
|
||||
}
|
||||
});
|
||||
|
||||
// Touch end
|
||||
modalContent.on('touchend', function(e) {
|
||||
if (!isDragging) return;
|
||||
|
||||
isDragging = false;
|
||||
var deltaY = currentY - startY;
|
||||
|
||||
modalContent.css('transition', 'transform 0.3s ease-out');
|
||||
|
||||
if (deltaY > threshold) {
|
||||
// Dismiss modal
|
||||
modalContent.css('transform', 'translateY(100%)');
|
||||
setTimeout(function() {
|
||||
modal.modal('hide');
|
||||
modalContent.css('transform', '');
|
||||
}, 300);
|
||||
} else {
|
||||
// Snap back
|
||||
modalContent.css('transform', '');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize form inputs based on device type (legacy compatibility)
|
||||
function initializeFormInputs() {
|
||||
// This function is maintained for backward compatibility with gasrecord.js
|
||||
// New modals should use initMobileModal() instead
|
||||
console.warn('initializeFormInputs() is deprecated. Use initMobileModal() instead.');
|
||||
}
|
||||
function getAndValidateSelectedVehicle() {
|
||||
var selectedVehiclesArray = [];
|
||||
$("#vehicleSelector :checked").map(function () {
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
// Initialize tax record modal for mobile (using shared mobile framework)
|
||||
function initializeTaxRecordMobile() {
|
||||
initMobileModal({
|
||||
modalId: '#taxRecordModal',
|
||||
dateInputId: '#taxRecordDate',
|
||||
tagSelectorId: '#taxRecordTag'
|
||||
});
|
||||
|
||||
// Handle desktop initialization
|
||||
if (!isMobileDevice()) {
|
||||
initDatePicker($('#taxRecordDate'));
|
||||
initTagSelector($("#taxRecordTag"));
|
||||
}
|
||||
}
|
||||
|
||||
function showAddTaxRecordModal() {
|
||||
$.get('/Vehicle/GetAddTaxRecordPartialView', function (data) {
|
||||
if (data) {
|
||||
$("#taxRecordModalContent").html(data);
|
||||
//initiate datepicker
|
||||
initDatePicker($('#taxRecordDate'));
|
||||
initTagSelector($("#taxRecordTag"));
|
||||
|
||||
// Initialize mobile experience using shared framework
|
||||
initializeTaxRecordMobile();
|
||||
|
||||
$('#taxRecordModal').modal('show');
|
||||
}
|
||||
});
|
||||
@@ -25,9 +41,10 @@ function showEditTaxRecordModal(taxRecordId, nocache) {
|
||||
$.get(`/Vehicle/GetTaxRecordForEditById?taxRecordId=${taxRecordId}`, function (data) {
|
||||
if (data) {
|
||||
$("#taxRecordModalContent").html(data);
|
||||
//initiate datepicker
|
||||
initDatePicker($('#taxRecordDate'));
|
||||
initTagSelector($("#taxRecordTag"));
|
||||
|
||||
// Initialize mobile experience using shared framework
|
||||
initializeTaxRecordMobile();
|
||||
|
||||
$('#taxRecordModal').modal('show');
|
||||
bindModalInputChanges('taxRecordModal');
|
||||
$('#taxRecordModal').off('shown.bs.modal').on('shown.bs.modal', function () {
|
||||
|
||||
@@ -1,10 +1,26 @@
|
||||
// Initialize upgrade record modal for mobile (using shared mobile framework)
|
||||
function initializeUpgradeRecordMobile() {
|
||||
initMobileModal({
|
||||
modalId: '#upgradeRecordModal',
|
||||
dateInputId: '#upgradeRecordDate',
|
||||
tagSelectorId: '#upgradeRecordTag'
|
||||
});
|
||||
|
||||
// Handle desktop initialization
|
||||
if (!isMobileDevice()) {
|
||||
initDatePicker($('#upgradeRecordDate'));
|
||||
initTagSelector($("#upgradeRecordTag"));
|
||||
}
|
||||
}
|
||||
|
||||
function showAddUpgradeRecordModal() {
|
||||
$.get('/Vehicle/GetAddUpgradeRecordPartialView', function (data) {
|
||||
if (data) {
|
||||
$("#upgradeRecordModalContent").html(data);
|
||||
//initiate datepicker
|
||||
initDatePicker($('#upgradeRecordDate'));
|
||||
initTagSelector($("#upgradeRecordTag"));
|
||||
|
||||
// Initialize mobile experience using shared framework
|
||||
initializeUpgradeRecordMobile();
|
||||
|
||||
$('#upgradeRecordModal').modal('show');
|
||||
}
|
||||
});
|
||||
@@ -25,9 +41,8 @@ function showEditUpgradeRecordModal(upgradeRecordId, nocache) {
|
||||
$.get(`/Vehicle/GetUpgradeRecordForEditById?upgradeRecordId=${upgradeRecordId}`, function (data) {
|
||||
if (data) {
|
||||
$("#upgradeRecordModalContent").html(data);
|
||||
//initiate datepicker
|
||||
initDatePicker($('#upgradeRecordDate'));
|
||||
initTagSelector($("#upgradeRecordTag"));
|
||||
// Initialize mobile experience using shared framework
|
||||
initializeUpgradeRecordMobile();
|
||||
$('#upgradeRecordModal').modal('show');
|
||||
bindModalInputChanges('upgradeRecordModal');
|
||||
$('#upgradeRecordModal').off('shown.bs.modal').on('shown.bs.modal', function () {
|
||||
|
||||
Reference in New Issue
Block a user