8 changed files with 2042 additions and 66 deletions
-
129weed/admin/dash/admin_server.go
-
15weed/admin/handlers/maintenance_handlers.go
-
488weed/admin/maintenance/config_schema.go
-
36weed/admin/maintenance/maintenance_types.go
-
389weed/admin/view/app/maintenance_config_schema.templ
-
772weed/admin/view/app/maintenance_config_schema_templ.go
-
69weed/admin/view/components/form_fields.templ
-
210weed/admin/view/components/form_fields_templ.go
@ -0,0 +1,488 @@ |
|||
package maintenance |
|||
|
|||
import ( |
|||
"fmt" |
|||
"reflect" |
|||
"strings" |
|||
"time" |
|||
) |
|||
|
|||
// ConfigFieldType defines the type of a configuration field
|
|||
type ConfigFieldType string |
|||
|
|||
const ( |
|||
FieldTypeBool ConfigFieldType = "bool" |
|||
FieldTypeInt ConfigFieldType = "int" |
|||
FieldTypeDuration ConfigFieldType = "duration" |
|||
FieldTypeInterval ConfigFieldType = "interval" |
|||
FieldTypeString ConfigFieldType = "string" |
|||
FieldTypeFloat ConfigFieldType = "float" |
|||
) |
|||
|
|||
// ConfigFieldUnit defines the unit for display purposes
|
|||
type ConfigFieldUnit string |
|||
|
|||
const ( |
|||
UnitSeconds ConfigFieldUnit = "seconds" |
|||
UnitMinutes ConfigFieldUnit = "minutes" |
|||
UnitHours ConfigFieldUnit = "hours" |
|||
UnitDays ConfigFieldUnit = "days" |
|||
UnitCount ConfigFieldUnit = "count" |
|||
UnitNone ConfigFieldUnit = "" |
|||
) |
|||
|
|||
// ConfigField defines a configuration field with all its metadata
|
|||
type ConfigField struct { |
|||
// Field identification
|
|||
Name string `json:"name"` |
|||
JSONName string `json:"json_name"` |
|||
Type ConfigFieldType `json:"type"` |
|||
|
|||
// Default value and validation
|
|||
DefaultValue interface{} `json:"default_value"` |
|||
MinValue interface{} `json:"min_value,omitempty"` |
|||
MaxValue interface{} `json:"max_value,omitempty"` |
|||
Required bool `json:"required"` |
|||
|
|||
// UI display
|
|||
DisplayName string `json:"display_name"` |
|||
Description string `json:"description"` |
|||
HelpText string `json:"help_text"` |
|||
Placeholder string `json:"placeholder"` |
|||
Unit ConfigFieldUnit `json:"unit"` |
|||
|
|||
// Form rendering
|
|||
InputType string `json:"input_type"` // "checkbox", "number", "text", etc.
|
|||
CSSClasses string `json:"css_classes,omitempty"` |
|||
} |
|||
|
|||
// GetDisplayValue returns the value formatted for display in the specified unit
|
|||
func (cf *ConfigField) GetDisplayValue(value interface{}) interface{} { |
|||
if (cf.Type == FieldTypeDuration || cf.Type == FieldTypeInterval) && cf.Unit != UnitSeconds { |
|||
if duration, ok := value.(time.Duration); ok { |
|||
switch cf.Unit { |
|||
case UnitMinutes: |
|||
return int(duration.Minutes()) |
|||
case UnitHours: |
|||
return int(duration.Hours()) |
|||
case UnitDays: |
|||
return int(duration.Hours() / 24) |
|||
} |
|||
} |
|||
if seconds, ok := value.(int); ok { |
|||
switch cf.Unit { |
|||
case UnitMinutes: |
|||
return seconds / 60 |
|||
case UnitHours: |
|||
return seconds / 3600 |
|||
case UnitDays: |
|||
return seconds / (24 * 3600) |
|||
} |
|||
} |
|||
} |
|||
return value |
|||
} |
|||
|
|||
// GetIntervalDisplayValue returns the value and unit for interval fields
|
|||
func (cf *ConfigField) GetIntervalDisplayValue(value interface{}) (int, string) { |
|||
if cf.Type != FieldTypeInterval { |
|||
return 0, "minutes" |
|||
} |
|||
|
|||
seconds := 0 |
|||
if duration, ok := value.(time.Duration); ok { |
|||
seconds = int(duration.Seconds()) |
|||
} else if s, ok := value.(int); ok { |
|||
seconds = s |
|||
} |
|||
|
|||
return SecondsToIntervalValueUnit(seconds) |
|||
} |
|||
|
|||
// SecondsToIntervalValueUnit converts seconds to the most appropriate interval unit
|
|||
func SecondsToIntervalValueUnit(totalSeconds int) (int, string) { |
|||
if totalSeconds == 0 { |
|||
return 0, "minutes" |
|||
} |
|||
|
|||
// Check if it's evenly divisible by days
|
|||
if totalSeconds%(24*3600) == 0 { |
|||
return totalSeconds / (24 * 3600), "days" |
|||
} |
|||
|
|||
// Check if it's evenly divisible by hours
|
|||
if totalSeconds%3600 == 0 { |
|||
return totalSeconds / 3600, "hours" |
|||
} |
|||
|
|||
// Default to minutes
|
|||
return totalSeconds / 60, "minutes" |
|||
} |
|||
|
|||
// IntervalValueUnitToSeconds converts interval value and unit to seconds
|
|||
func IntervalValueUnitToSeconds(value int, unit string) int { |
|||
switch unit { |
|||
case "days": |
|||
return value * 24 * 3600 |
|||
case "hours": |
|||
return value * 3600 |
|||
case "minutes": |
|||
return value * 60 |
|||
default: |
|||
return value * 60 // Default to minutes
|
|||
} |
|||
} |
|||
|
|||
// ParseDisplayValue converts a display value back to the storage format
|
|||
func (cf *ConfigField) ParseDisplayValue(displayValue interface{}) interface{} { |
|||
if (cf.Type == FieldTypeDuration || cf.Type == FieldTypeInterval) && cf.Unit != UnitSeconds { |
|||
if val, ok := displayValue.(int); ok { |
|||
switch cf.Unit { |
|||
case UnitMinutes: |
|||
return val * 60 |
|||
case UnitHours: |
|||
return val * 3600 |
|||
case UnitDays: |
|||
return val * 24 * 3600 |
|||
} |
|||
} |
|||
} |
|||
return displayValue |
|||
} |
|||
|
|||
// ParseIntervalFormData parses form data for interval fields (value + unit)
|
|||
func (cf *ConfigField) ParseIntervalFormData(valueStr, unitStr string) (int, error) { |
|||
if cf.Type != FieldTypeInterval { |
|||
return 0, fmt.Errorf("field %s is not an interval field", cf.Name) |
|||
} |
|||
|
|||
value := 0 |
|||
if valueStr != "" { |
|||
var err error |
|||
value, err = fmt.Sscanf(valueStr, "%d", &value) |
|||
if err != nil { |
|||
return 0, fmt.Errorf("invalid interval value: %s", valueStr) |
|||
} |
|||
} |
|||
|
|||
return IntervalValueUnitToSeconds(value, unitStr), nil |
|||
} |
|||
|
|||
// ValidateValue validates a value against the field constraints
|
|||
func (cf *ConfigField) ValidateValue(value interface{}) error { |
|||
if cf.Required && (value == nil || value == "" || value == 0) { |
|||
return fmt.Errorf("%s is required", cf.DisplayName) |
|||
} |
|||
|
|||
if cf.MinValue != nil { |
|||
if !cf.compareValues(value, cf.MinValue, ">=") { |
|||
return fmt.Errorf("%s must be >= %v", cf.DisplayName, cf.MinValue) |
|||
} |
|||
} |
|||
|
|||
if cf.MaxValue != nil { |
|||
if !cf.compareValues(value, cf.MaxValue, "<=") { |
|||
return fmt.Errorf("%s must be <= %v", cf.DisplayName, cf.MaxValue) |
|||
} |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
|
|||
// compareValues compares two values based on the operator
|
|||
func (cf *ConfigField) compareValues(a, b interface{}, op string) bool { |
|||
switch cf.Type { |
|||
case FieldTypeInt: |
|||
aVal, aOk := a.(int) |
|||
bVal, bOk := b.(int) |
|||
if !aOk || !bOk { |
|||
return false |
|||
} |
|||
switch op { |
|||
case ">=": |
|||
return aVal >= bVal |
|||
case "<=": |
|||
return aVal <= bVal |
|||
} |
|||
case FieldTypeFloat: |
|||
aVal, aOk := a.(float64) |
|||
bVal, bOk := b.(float64) |
|||
if !aOk || !bOk { |
|||
return false |
|||
} |
|||
switch op { |
|||
case ">=": |
|||
return aVal >= bVal |
|||
case "<=": |
|||
return aVal <= bVal |
|||
} |
|||
} |
|||
return true |
|||
} |
|||
|
|||
// MaintenanceConfigSchema defines the schema for maintenance configuration
|
|||
type MaintenanceConfigSchema struct { |
|||
Fields map[string]*ConfigField `json:"fields"` |
|||
} |
|||
|
|||
// GetMaintenanceConfigSchema returns the schema for maintenance configuration
|
|||
func GetMaintenanceConfigSchema() *MaintenanceConfigSchema { |
|||
return &MaintenanceConfigSchema{ |
|||
Fields: map[string]*ConfigField{ |
|||
"enabled": { |
|||
Name: "enabled", |
|||
JSONName: "enabled", |
|||
Type: FieldTypeBool, |
|||
DefaultValue: false, |
|||
Required: false, |
|||
DisplayName: "Enable Maintenance System", |
|||
Description: "When enabled, the system will automatically scan for and execute maintenance tasks", |
|||
HelpText: "Toggle this to enable or disable the entire maintenance system", |
|||
InputType: "checkbox", |
|||
CSSClasses: "form-check-input", |
|||
}, |
|||
"scan_interval_seconds": { |
|||
Name: "scan_interval_seconds", |
|||
JSONName: "scan_interval_seconds", |
|||
Type: FieldTypeInterval, |
|||
DefaultValue: 30 * 60, // 30 minutes in seconds
|
|||
MinValue: 1 * 60, // 1 minute
|
|||
MaxValue: 24 * 60 * 60, // 24 hours
|
|||
Required: true, |
|||
DisplayName: "Scan Interval", |
|||
Description: "How often to scan for maintenance tasks", |
|||
HelpText: "The system will check for new maintenance tasks at this interval", |
|||
Placeholder: "30", |
|||
Unit: UnitMinutes, |
|||
InputType: "interval", |
|||
CSSClasses: "form-control", |
|||
}, |
|||
"worker_timeout_seconds": { |
|||
Name: "worker_timeout_seconds", |
|||
JSONName: "worker_timeout_seconds", |
|||
Type: FieldTypeInterval, |
|||
DefaultValue: 5 * 60, // 5 minutes
|
|||
MinValue: 1 * 60, // 1 minute
|
|||
MaxValue: 60 * 60, // 1 hour
|
|||
Required: true, |
|||
DisplayName: "Worker Timeout", |
|||
Description: "How long to wait for worker heartbeat before considering it inactive", |
|||
HelpText: "Workers that don't send heartbeats within this time are considered offline", |
|||
Placeholder: "5", |
|||
Unit: UnitMinutes, |
|||
InputType: "interval", |
|||
CSSClasses: "form-control", |
|||
}, |
|||
"task_timeout_seconds": { |
|||
Name: "task_timeout_seconds", |
|||
JSONName: "task_timeout_seconds", |
|||
Type: FieldTypeInterval, |
|||
DefaultValue: 2 * 60 * 60, // 2 hours
|
|||
MinValue: 1 * 60 * 60, // 1 hour
|
|||
MaxValue: 24 * 60 * 60, // 24 hours
|
|||
Required: true, |
|||
DisplayName: "Task Timeout", |
|||
Description: "Maximum time allowed for a single task to complete", |
|||
HelpText: "Tasks that run longer than this will be considered failed and may be retried", |
|||
Placeholder: "2", |
|||
Unit: UnitHours, |
|||
InputType: "interval", |
|||
CSSClasses: "form-control", |
|||
}, |
|||
"retry_delay_seconds": { |
|||
Name: "retry_delay_seconds", |
|||
JSONName: "retry_delay_seconds", |
|||
Type: FieldTypeInterval, |
|||
DefaultValue: 15 * 60, // 15 minutes
|
|||
MinValue: 1 * 60, // 1 minute
|
|||
MaxValue: 2 * 60 * 60, // 2 hours
|
|||
Required: true, |
|||
DisplayName: "Retry Delay", |
|||
Description: "Time to wait before retrying failed tasks", |
|||
HelpText: "Failed tasks will wait this long before being retried", |
|||
Placeholder: "15", |
|||
Unit: UnitMinutes, |
|||
InputType: "interval", |
|||
CSSClasses: "form-control", |
|||
}, |
|||
"max_retries": { |
|||
Name: "max_retries", |
|||
JSONName: "max_retries", |
|||
Type: FieldTypeInt, |
|||
DefaultValue: 3, |
|||
MinValue: 0, |
|||
MaxValue: 10, |
|||
Required: false, |
|||
DisplayName: "Default Max Retries", |
|||
Description: "Default number of times to retry failed tasks", |
|||
HelpText: "Tasks will be retried this many times before being marked as permanently failed", |
|||
Placeholder: "3 (default)", |
|||
Unit: UnitCount, |
|||
InputType: "number", |
|||
CSSClasses: "form-control", |
|||
}, |
|||
"cleanup_interval_seconds": { |
|||
Name: "cleanup_interval_seconds", |
|||
JSONName: "cleanup_interval_seconds", |
|||
Type: FieldTypeInterval, |
|||
DefaultValue: 24 * 60 * 60, // 24 hours
|
|||
MinValue: 1 * 60 * 60, // 1 hour
|
|||
MaxValue: 7 * 24 * 60 * 60, // 7 days
|
|||
Required: true, |
|||
DisplayName: "Cleanup Interval", |
|||
Description: "How often to clean up old task records", |
|||
HelpText: "The system will remove old completed/failed tasks at this interval", |
|||
Placeholder: "24", |
|||
Unit: UnitHours, |
|||
InputType: "interval", |
|||
CSSClasses: "form-control", |
|||
}, |
|||
"task_retention_seconds": { |
|||
Name: "task_retention_seconds", |
|||
JSONName: "task_retention_seconds", |
|||
Type: FieldTypeInterval, |
|||
DefaultValue: 7 * 24 * 60 * 60, // 7 days
|
|||
MinValue: 1 * 24 * 60 * 60, // 1 day
|
|||
MaxValue: 30 * 24 * 60 * 60, // 30 days
|
|||
Required: true, |
|||
DisplayName: "Task Retention", |
|||
Description: "How long to keep completed/failed task records", |
|||
HelpText: "Task records older than this will be automatically deleted", |
|||
Placeholder: "7", |
|||
Unit: UnitDays, |
|||
InputType: "interval", |
|||
CSSClasses: "form-control", |
|||
}, |
|||
"global_max_concurrent": { |
|||
Name: "global_max_concurrent", |
|||
JSONName: "global_max_concurrent", |
|||
Type: FieldTypeInt, |
|||
DefaultValue: 4, |
|||
MinValue: 1, |
|||
MaxValue: 20, |
|||
Required: true, |
|||
DisplayName: "Global Concurrent Limit", |
|||
Description: "Maximum number of maintenance tasks that can run simultaneously across all workers", |
|||
HelpText: "This limits the total system load from maintenance operations", |
|||
Placeholder: "4 (default)", |
|||
Unit: UnitCount, |
|||
InputType: "number", |
|||
CSSClasses: "form-control", |
|||
}, |
|||
}, |
|||
} |
|||
} |
|||
|
|||
// ApplyDefaults applies default values to a configuration struct using reflection
|
|||
func (schema *MaintenanceConfigSchema) ApplyDefaults(config interface{}) error { |
|||
configValue := reflect.ValueOf(config) |
|||
if configValue.Kind() == reflect.Ptr { |
|||
configValue = configValue.Elem() |
|||
} |
|||
|
|||
if configValue.Kind() != reflect.Struct { |
|||
return fmt.Errorf("config must be a struct or pointer to struct") |
|||
} |
|||
|
|||
configType := configValue.Type() |
|||
|
|||
for i := 0; i < configValue.NumField(); i++ { |
|||
field := configValue.Field(i) |
|||
fieldType := configType.Field(i) |
|||
|
|||
// Get JSON tag name
|
|||
jsonTag := fieldType.Tag.Get("json") |
|||
if jsonTag == "" { |
|||
continue |
|||
} |
|||
|
|||
// Remove options like ",omitempty"
|
|||
if commaIdx := strings.Index(jsonTag, ","); commaIdx > 0 { |
|||
jsonTag = jsonTag[:commaIdx] |
|||
} |
|||
|
|||
// Find corresponding schema field
|
|||
schemaField, exists := schema.Fields[jsonTag] |
|||
if !exists { |
|||
continue |
|||
} |
|||
|
|||
// Apply default if field is zero value
|
|||
if field.CanSet() && isZeroValue(field) { |
|||
defaultValue := reflect.ValueOf(schemaField.DefaultValue) |
|||
if defaultValue.Type().ConvertibleTo(field.Type()) { |
|||
field.Set(defaultValue.Convert(field.Type())) |
|||
} |
|||
} |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
|
|||
// isZeroValue checks if a reflect.Value represents a zero value
|
|||
func isZeroValue(v reflect.Value) bool { |
|||
switch v.Kind() { |
|||
case reflect.Bool: |
|||
return !v.Bool() |
|||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
|||
return v.Int() == 0 |
|||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: |
|||
return v.Uint() == 0 |
|||
case reflect.Float32, reflect.Float64: |
|||
return v.Float() == 0 |
|||
case reflect.String: |
|||
return v.String() == "" |
|||
case reflect.Slice, reflect.Map, reflect.Array: |
|||
return v.IsNil() || v.Len() == 0 |
|||
case reflect.Interface, reflect.Ptr: |
|||
return v.IsNil() |
|||
} |
|||
return false |
|||
} |
|||
|
|||
// ValidateConfig validates a configuration against the schema
|
|||
func (schema *MaintenanceConfigSchema) ValidateConfig(config interface{}) []error { |
|||
var errors []error |
|||
|
|||
configValue := reflect.ValueOf(config) |
|||
if configValue.Kind() == reflect.Ptr { |
|||
configValue = configValue.Elem() |
|||
} |
|||
|
|||
if configValue.Kind() != reflect.Struct { |
|||
errors = append(errors, fmt.Errorf("config must be a struct or pointer to struct")) |
|||
return errors |
|||
} |
|||
|
|||
configType := configValue.Type() |
|||
|
|||
for i := 0; i < configValue.NumField(); i++ { |
|||
field := configValue.Field(i) |
|||
fieldType := configType.Field(i) |
|||
|
|||
// Get JSON tag name
|
|||
jsonTag := fieldType.Tag.Get("json") |
|||
if jsonTag == "" { |
|||
continue |
|||
} |
|||
|
|||
// Remove options like ",omitempty"
|
|||
if commaIdx := strings.Index(jsonTag, ","); commaIdx > 0 { |
|||
jsonTag = jsonTag[:commaIdx] |
|||
} |
|||
|
|||
// Find corresponding schema field
|
|||
schemaField, exists := schema.Fields[jsonTag] |
|||
if !exists { |
|||
continue |
|||
} |
|||
|
|||
// Validate field value
|
|||
fieldValue := field.Interface() |
|||
if err := schemaField.ValidateValue(fieldValue); err != nil { |
|||
errors = append(errors, err) |
|||
} |
|||
} |
|||
|
|||
return errors |
|||
} |
|||
@ -0,0 +1,389 @@ |
|||
package app |
|||
|
|||
import ( |
|||
"fmt" |
|||
"github.com/seaweedfs/seaweedfs/weed/admin/maintenance" |
|||
) |
|||
|
|||
templ MaintenanceConfigSchema(data *maintenance.MaintenanceConfigData, schema *maintenance.MaintenanceConfigSchema) { |
|||
<div class="container-fluid"> |
|||
<div class="row mb-4"> |
|||
<div class="col-12"> |
|||
<div class="d-flex justify-content-between align-items-center"> |
|||
<h2 class="mb-0"> |
|||
<i class="fas fa-cog me-2"></i> |
|||
Maintenance Configuration (SCHEMA_TEMPLATE) |
|||
</h2> |
|||
<div class="btn-group"> |
|||
<a href="/maintenance" class="btn btn-outline-secondary"> |
|||
<i class="fas fa-arrow-left me-1"></i> |
|||
Back to Queue |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="row"> |
|||
<div class="col-12"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
<h5 class="mb-0">System Settings</h5> |
|||
</div> |
|||
<div class="card-body"> |
|||
<form id="maintenanceConfigForm"> |
|||
<!-- Dynamically render all schema fields --> |
|||
@ConfigField(schema.Fields["enabled"], data.Config.Enabled) |
|||
@ConfigField(schema.Fields["scan_interval_seconds"], data.Config.ScanIntervalSeconds) |
|||
@ConfigField(schema.Fields["worker_timeout_seconds"], data.Config.WorkerTimeoutSeconds) |
|||
@ConfigField(schema.Fields["task_timeout_seconds"], data.Config.TaskTimeoutSeconds) |
|||
@ConfigField(schema.Fields["retry_delay_seconds"], data.Config.RetryDelaySeconds) |
|||
@ConfigField(schema.Fields["max_retries"], data.Config.MaxRetries) |
|||
@ConfigField(schema.Fields["cleanup_interval_seconds"], data.Config.CleanupIntervalSeconds) |
|||
@ConfigField(schema.Fields["task_retention_seconds"], data.Config.TaskRetentionSeconds) |
|||
if data.Config.Policy != nil { |
|||
@ConfigField(schema.Fields["global_max_concurrent"], data.Config.Policy.GlobalMaxConcurrent) |
|||
} |
|||
|
|||
<div class="d-flex gap-2"> |
|||
<button type="button" class="btn btn-primary" onclick="saveConfiguration()"> |
|||
<i class="fas fa-save me-1"></i> |
|||
Save Configuration |
|||
</button> |
|||
<button type="button" class="btn btn-secondary" onclick="resetToDefaults()"> |
|||
<i class="fas fa-undo me-1"></i> |
|||
Reset to Defaults |
|||
</button> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- Individual Task Configuration Menu --> |
|||
<div class="row mt-4"> |
|||
<div class="col-12"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
<h5 class="mb-0"> |
|||
<i class="fas fa-cogs me-2"></i> |
|||
Task Configuration |
|||
</h5> |
|||
</div> |
|||
<div class="card-body"> |
|||
<p class="text-muted mb-3">Configure specific settings for each maintenance task type.</p> |
|||
<div class="list-group"> |
|||
for _, menuItem := range data.MenuItems { |
|||
<a href={templ.SafeURL(menuItem.Path)} class="list-group-item list-group-item-action"> |
|||
<div class="d-flex w-100 justify-content-between"> |
|||
<h6 class="mb-1"> |
|||
<i class={menuItem.Icon + " me-2"}></i> |
|||
{menuItem.DisplayName} |
|||
</h6> |
|||
if menuItem.IsEnabled { |
|||
<span class="badge bg-success">Enabled</span> |
|||
} else { |
|||
<span class="badge bg-secondary">Disabled</span> |
|||
} |
|||
</div> |
|||
<p class="mb-1 small text-muted">{menuItem.Description}</p> |
|||
</a> |
|||
} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- Statistics Overview --> |
|||
<div class="row mt-4"> |
|||
<div class="col-12"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
<h5 class="mb-0">System Statistics</h5> |
|||
</div> |
|||
<div class="card-body"> |
|||
<div class="row"> |
|||
<div class="col-md-3"> |
|||
<div class="text-center"> |
|||
<h6 class="text-muted">Last Scan</h6> |
|||
<p class="mb-0">{data.LastScanTime.Format("2006-01-02 15:04:05")}</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3"> |
|||
<div class="text-center"> |
|||
<h6 class="text-muted">Next Scan</h6> |
|||
<p class="mb-0">{data.NextScanTime.Format("2006-01-02 15:04:05")}</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3"> |
|||
<div class="text-center"> |
|||
<h6 class="text-muted">Total Tasks</h6> |
|||
<p class="mb-0">{fmt.Sprintf("%d", data.SystemStats.TotalTasks)}</p> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-3"> |
|||
<div class="text-center"> |
|||
<h6 class="text-muted">Active Workers</h6> |
|||
<p class="mb-0">{fmt.Sprintf("%d", data.SystemStats.ActiveWorkers)}</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<script> |
|||
function saveConfiguration() { |
|||
const form = document.getElementById('maintenanceConfigForm'); |
|||
const formData = new FormData(form); |
|||
const config = {}; |
|||
|
|||
// Convert FormData to config object with proper type conversion |
|||
for (const [key, value] of formData.entries()) { |
|||
const field = getSchemaField(key); |
|||
if (field) { |
|||
config[key] = convertFieldValue(field, value); |
|||
} |
|||
} |
|||
|
|||
// Send the configuration |
|||
fetch('/api/maintenance/config', { |
|||
method: 'PUT', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
body: JSON.stringify(config) |
|||
}) |
|||
.then(response => response.json()) |
|||
.then(data => { |
|||
if (data.success) { |
|||
alert('Configuration saved successfully'); |
|||
location.reload(); // Reload to show updated values |
|||
} else { |
|||
alert('Failed to save configuration: ' + (data.error || 'Unknown error')); |
|||
} |
|||
}) |
|||
.catch(error => { |
|||
alert('Error: ' + error.message); |
|||
}); |
|||
} |
|||
|
|||
function resetToDefaults() { |
|||
if (confirm('Are you sure you want to reset to default configuration? This will overwrite your current settings.')) { |
|||
// Get schema defaults and apply them |
|||
const schema = window.maintenanceConfigSchema || {}; |
|||
Object.keys(schema.fields || {}).forEach(fieldName => { |
|||
const field = schema.fields[fieldName]; |
|||
const element = document.getElementById(fieldName); |
|||
if (element && field.default_value !== undefined) { |
|||
if (field.input_type === 'checkbox') { |
|||
element.checked = field.default_value; |
|||
} else { |
|||
element.value = field.GetDisplayValue ? field.GetDisplayValue(field.default_value) : field.default_value; |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
function getSchemaField(fieldName) { |
|||
const schema = window.maintenanceConfigSchema || {}; |
|||
return schema.fields && schema.fields[fieldName]; |
|||
} |
|||
|
|||
function convertFieldValue(field, value) { |
|||
switch (field.type) { |
|||
case 'bool': |
|||
return value === 'on' || value === 'true'; |
|||
case 'int': |
|||
const intVal = parseInt(value); |
|||
return field.ParseDisplayValue ? field.ParseDisplayValue(intVal) : intVal; |
|||
case 'float': |
|||
return parseFloat(value); |
|||
default: |
|||
return value; |
|||
} |
|||
} |
|||
|
|||
// Store schema data for JavaScript access |
|||
window.maintenanceConfigSchema = @schemaToJSON(schema); |
|||
</script> |
|||
} |
|||
|
|||
// ConfigField renders a single configuration field based on schema |
|||
templ ConfigField(field *maintenance.ConfigField, value interface{}) { |
|||
if field.InputType == "interval" { |
|||
<!-- Interval field with number input + unit dropdown --> |
|||
<div class="mb-3"> |
|||
<label for={ field.JSONName } class="form-label"> |
|||
{ field.DisplayName } |
|||
if field.Required { |
|||
<span class="text-danger">*</span> |
|||
} |
|||
</label> |
|||
<div class="input-group"> |
|||
<input |
|||
type="number" |
|||
class="form-control" |
|||
id={ field.JSONName + "_value" } |
|||
name={ field.JSONName + "_value" } |
|||
value={ fmt.Sprintf("%.0f", convertSecondsToDisplayValue(value, field)) } |
|||
step="1" |
|||
min="1" |
|||
if field.Required { |
|||
required |
|||
} |
|||
/> |
|||
<select |
|||
class="form-select" |
|||
id={ field.JSONName + "_unit" } |
|||
name={ field.JSONName + "_unit" } |
|||
style="max-width: 120px;" |
|||
if field.Required { |
|||
required |
|||
} |
|||
> |
|||
<option |
|||
value="minutes" |
|||
if getDisplayUnit(value, field) == "minutes" { |
|||
selected |
|||
} |
|||
> |
|||
Minutes |
|||
</option> |
|||
<option |
|||
value="hours" |
|||
if getDisplayUnit(value, field) == "hours" { |
|||
selected |
|||
} |
|||
> |
|||
Hours |
|||
</option> |
|||
<option |
|||
value="days" |
|||
if getDisplayUnit(value, field) == "days" { |
|||
selected |
|||
} |
|||
> |
|||
Days |
|||
</option> |
|||
</select> |
|||
</div> |
|||
if field.Description != "" { |
|||
<div class="form-text text-muted">{ field.Description }</div> |
|||
} |
|||
</div> |
|||
} else if field.InputType == "checkbox" { |
|||
<!-- Checkbox field --> |
|||
<div class="mb-3"> |
|||
<div class="form-check form-switch"> |
|||
<input |
|||
class="form-check-input" |
|||
type="checkbox" |
|||
id={ field.JSONName } |
|||
name={ field.JSONName } |
|||
if getBoolValue(value) { |
|||
checked |
|||
} |
|||
/> |
|||
<label class="form-check-label" for={ field.JSONName }> |
|||
<strong>{ field.DisplayName }</strong> |
|||
</label> |
|||
</div> |
|||
if field.Description != "" { |
|||
<div class="form-text text-muted">{ field.Description }</div> |
|||
} |
|||
</div> |
|||
} else { |
|||
<!-- Number field --> |
|||
<div class="mb-3"> |
|||
<label for={ field.JSONName } class="form-label"> |
|||
{ field.DisplayName } |
|||
if field.Required { |
|||
<span class="text-danger">*</span> |
|||
} |
|||
</label> |
|||
<input |
|||
type="number" |
|||
class="form-control" |
|||
id={ field.JSONName } |
|||
name={ field.JSONName } |
|||
value={ fmt.Sprintf("%v", value) } |
|||
placeholder={ field.Placeholder } |
|||
if field.MinValue != nil { |
|||
min={ fmt.Sprintf("%v", field.MinValue) } |
|||
} |
|||
if field.MaxValue != nil { |
|||
max={ fmt.Sprintf("%v", field.MaxValue) } |
|||
} |
|||
if field.Required { |
|||
required |
|||
} |
|||
/> |
|||
if field.Description != "" { |
|||
<div class="form-text text-muted">{ field.Description }</div> |
|||
} |
|||
</div> |
|||
} |
|||
} |
|||
|
|||
// Helper functions for the template |
|||
func getBoolValue(value interface{}) bool { |
|||
if boolVal, ok := value.(bool); ok { |
|||
return boolVal |
|||
} |
|||
return false |
|||
} |
|||
|
|||
func convertSecondsToDisplayValue(value interface{}, field *maintenance.ConfigField) float64 { |
|||
if intVal, ok := value.(int); ok { |
|||
if intVal == 0 { |
|||
return 0 |
|||
} |
|||
|
|||
// Check if it's evenly divisible by days |
|||
if intVal%(24*3600) == 0 { |
|||
return float64(intVal / (24 * 3600)) |
|||
} |
|||
|
|||
// Check if it's evenly divisible by hours |
|||
if intVal%3600 == 0 { |
|||
return float64(intVal / 3600) |
|||
} |
|||
|
|||
// Default to minutes |
|||
return float64(intVal / 60) |
|||
} |
|||
return 0 |
|||
} |
|||
|
|||
func getDisplayUnit(value interface{}, field *maintenance.ConfigField) string { |
|||
if intVal, ok := value.(int); ok { |
|||
if intVal == 0 { |
|||
return "minutes" |
|||
} |
|||
|
|||
// Check if it's evenly divisible by days |
|||
if intVal%(24*3600) == 0 { |
|||
return "days" |
|||
} |
|||
|
|||
// Check if it's evenly divisible by hours |
|||
if intVal%3600 == 0 { |
|||
return "hours" |
|||
} |
|||
|
|||
// Default to minutes |
|||
return "minutes" |
|||
} |
|||
return "minutes" |
|||
} |
|||
|
|||
// Helper function to convert schema to JSON for JavaScript |
|||
templ schemaToJSON(schema *maintenance.MaintenanceConfigSchema) { |
|||
{`{}`} |
|||
} |
|||
@ -0,0 +1,772 @@ |
|||
// Code generated by templ - DO NOT EDIT.
|
|||
|
|||
// templ: version: v0.3.906
|
|||
package app |
|||
|
|||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
|||
|
|||
import "github.com/a-h/templ" |
|||
import templruntime "github.com/a-h/templ/runtime" |
|||
|
|||
import ( |
|||
"fmt" |
|||
"github.com/seaweedfs/seaweedfs/weed/admin/maintenance" |
|||
) |
|||
|
|||
func MaintenanceConfigSchema(data *maintenance.MaintenanceConfigData, schema *maintenance.MaintenanceConfigSchema) templ.Component { |
|||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { |
|||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context |
|||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { |
|||
return templ_7745c5c3_CtxErr |
|||
} |
|||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) |
|||
if !templ_7745c5c3_IsBuffer { |
|||
defer func() { |
|||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) |
|||
if templ_7745c5c3_Err == nil { |
|||
templ_7745c5c3_Err = templ_7745c5c3_BufErr |
|||
} |
|||
}() |
|||
} |
|||
ctx = templ.InitializeContext(ctx) |
|||
templ_7745c5c3_Var1 := templ.GetChildren(ctx) |
|||
if templ_7745c5c3_Var1 == nil { |
|||
templ_7745c5c3_Var1 = templ.NopComponent |
|||
} |
|||
ctx = templ.ClearChildren(ctx) |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"container-fluid\"><div class=\"row mb-4\"><div class=\"col-12\"><div class=\"d-flex justify-content-between align-items-center\"><h2 class=\"mb-0\"><i class=\"fas fa-cog me-2\"></i> Maintenance Configuration (SCHEMA_TEMPLATE)</h2><div class=\"btn-group\"><a href=\"/maintenance\" class=\"btn btn-outline-secondary\"><i class=\"fas fa-arrow-left me-1\"></i> Back to Queue</a></div></div></div></div><div class=\"row\"><div class=\"col-12\"><div class=\"card\"><div class=\"card-header\"><h5 class=\"mb-0\">System Settings</h5></div><div class=\"card-body\"><form id=\"maintenanceConfigForm\"><!-- Dynamically render all schema fields -->") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = ConfigField(schema.Fields["enabled"], data.Config.Enabled).Render(ctx, templ_7745c5c3_Buffer) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = ConfigField(schema.Fields["scan_interval_seconds"], data.Config.ScanIntervalSeconds).Render(ctx, templ_7745c5c3_Buffer) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = ConfigField(schema.Fields["worker_timeout_seconds"], data.Config.WorkerTimeoutSeconds).Render(ctx, templ_7745c5c3_Buffer) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = ConfigField(schema.Fields["task_timeout_seconds"], data.Config.TaskTimeoutSeconds).Render(ctx, templ_7745c5c3_Buffer) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = ConfigField(schema.Fields["retry_delay_seconds"], data.Config.RetryDelaySeconds).Render(ctx, templ_7745c5c3_Buffer) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = ConfigField(schema.Fields["max_retries"], data.Config.MaxRetries).Render(ctx, templ_7745c5c3_Buffer) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = ConfigField(schema.Fields["cleanup_interval_seconds"], data.Config.CleanupIntervalSeconds).Render(ctx, templ_7745c5c3_Buffer) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = ConfigField(schema.Fields["task_retention_seconds"], data.Config.TaskRetentionSeconds).Render(ctx, templ_7745c5c3_Buffer) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
if data.Config.Policy != nil { |
|||
templ_7745c5c3_Err = ConfigField(schema.Fields["global_max_concurrent"], data.Config.Policy.GlobalMaxConcurrent).Render(ctx, templ_7745c5c3_Buffer) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<div class=\"d-flex gap-2\"><button type=\"button\" class=\"btn btn-primary\" onclick=\"saveConfiguration()\"><i class=\"fas fa-save me-1\"></i> Save Configuration</button> <button type=\"button\" class=\"btn btn-secondary\" onclick=\"resetToDefaults()\"><i class=\"fas fa-undo me-1\"></i> Reset to Defaults</button></div></form></div></div></div></div><!-- Individual Task Configuration Menu --><div class=\"row mt-4\"><div class=\"col-12\"><div class=\"card\"><div class=\"card-header\"><h5 class=\"mb-0\"><i class=\"fas fa-cogs me-2\"></i> Task Configuration</h5></div><div class=\"card-body\"><p class=\"text-muted mb-3\">Configure specific settings for each maintenance task type.</p><div class=\"list-group\">") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
for _, menuItem := range data.MenuItems { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<a href=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var2 templ.SafeURL |
|||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(menuItem.Path)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 78, Col: 69} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" class=\"list-group-item list-group-item-action\"><div class=\"d-flex w-100 justify-content-between\"><h6 class=\"mb-1\">") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var3 = []any{menuItem.Icon + " me-2"} |
|||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var3...) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "<i class=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var4 string |
|||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var3).String()) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 1, Col: 0} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\"></i> ") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var5 string |
|||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(menuItem.DisplayName) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 82, Col: 65} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</h6>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
if menuItem.IsEnabled { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "<span class=\"badge bg-success\">Enabled</span>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} else { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<span class=\"badge bg-secondary\">Disabled</span>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</div><p class=\"mb-1 small text-muted\">") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var6 string |
|||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(menuItem.Description) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 90, Col: 90} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "</p></a>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</div></div></div></div></div><!-- Statistics Overview --><div class=\"row mt-4\"><div class=\"col-12\"><div class=\"card\"><div class=\"card-header\"><h5 class=\"mb-0\">System Statistics</h5></div><div class=\"card-body\"><div class=\"row\"><div class=\"col-md-3\"><div class=\"text-center\"><h6 class=\"text-muted\">Last Scan</h6><p class=\"mb-0\">") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var7 string |
|||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(data.LastScanTime.Format("2006-01-02 15:04:05")) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 111, Col: 100} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "</p></div></div><div class=\"col-md-3\"><div class=\"text-center\"><h6 class=\"text-muted\">Next Scan</h6><p class=\"mb-0\">") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var8 string |
|||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(data.NextScanTime.Format("2006-01-02 15:04:05")) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 117, Col: 100} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</p></div></div><div class=\"col-md-3\"><div class=\"text-center\"><h6 class=\"text-muted\">Total Tasks</h6><p class=\"mb-0\">") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var9 string |
|||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.SystemStats.TotalTasks)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 123, Col: 99} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</p></div></div><div class=\"col-md-3\"><div class=\"text-center\"><h6 class=\"text-muted\">Active Workers</h6><p class=\"mb-0\">") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var10 string |
|||
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", data.SystemStats.ActiveWorkers)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 129, Col: 102} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "</p></div></div></div></div></div></div></div></div><script>\n function saveConfiguration() {\n const form = document.getElementById('maintenanceConfigForm');\n const formData = new FormData(form);\n const config = {};\n\n // Convert FormData to config object with proper type conversion\n for (const [key, value] of formData.entries()) {\n const field = getSchemaField(key);\n if (field) {\n config[key] = convertFieldValue(field, value);\n }\n }\n\n // Send the configuration\n fetch('/api/maintenance/config', {\n method: 'PUT',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(config)\n })\n .then(response => response.json())\n .then(data => {\n if (data.success) {\n alert('Configuration saved successfully');\n location.reload(); // Reload to show updated values\n } else {\n alert('Failed to save configuration: ' + (data.error || 'Unknown error'));\n }\n })\n .catch(error => {\n alert('Error: ' + error.message);\n });\n }\n\n function resetToDefaults() {\n if (confirm('Are you sure you want to reset to default configuration? This will overwrite your current settings.')) {\n // Get schema defaults and apply them\n const schema = window.maintenanceConfigSchema || {};\n Object.keys(schema.fields || {}).forEach(fieldName => {\n const field = schema.fields[fieldName];\n const element = document.getElementById(fieldName);\n if (element && field.default_value !== undefined) {\n if (field.input_type === 'checkbox') {\n element.checked = field.default_value;\n } else {\n element.value = field.GetDisplayValue ? field.GetDisplayValue(field.default_value) : field.default_value;\n }\n }\n });\n }\n }\n\n function getSchemaField(fieldName) {\n const schema = window.maintenanceConfigSchema || {};\n return schema.fields && schema.fields[fieldName];\n }\n\n function convertFieldValue(field, value) {\n switch (field.type) {\n case 'bool':\n return value === 'on' || value === 'true';\n case 'int':\n const intVal = parseInt(value);\n return field.ParseDisplayValue ? field.ParseDisplayValue(intVal) : intVal;\n case 'float':\n return parseFloat(value);\n default:\n return value;\n }\n }\n\n // Store schema data for JavaScript access\n window.maintenanceConfigSchema = @schemaToJSON(schema);\n </script>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
return nil |
|||
}) |
|||
} |
|||
|
|||
// ConfigField renders a single configuration field based on schema
|
|||
func ConfigField(field *maintenance.ConfigField, value interface{}) templ.Component { |
|||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { |
|||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context |
|||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { |
|||
return templ_7745c5c3_CtxErr |
|||
} |
|||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) |
|||
if !templ_7745c5c3_IsBuffer { |
|||
defer func() { |
|||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) |
|||
if templ_7745c5c3_Err == nil { |
|||
templ_7745c5c3_Err = templ_7745c5c3_BufErr |
|||
} |
|||
}() |
|||
} |
|||
ctx = templ.InitializeContext(ctx) |
|||
templ_7745c5c3_Var11 := templ.GetChildren(ctx) |
|||
if templ_7745c5c3_Var11 == nil { |
|||
templ_7745c5c3_Var11 = templ.NopComponent |
|||
} |
|||
ctx = templ.ClearChildren(ctx) |
|||
if field.InputType == "interval" { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "<!-- Interval field with number input + unit dropdown --> <div class=\"mb-3\"><label for=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var12 string |
|||
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(field.JSONName) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 222, Col: 39} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\" class=\"form-label\">") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var13 string |
|||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(field.DisplayName) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 223, Col: 35} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, " ") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
if field.Required { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<span class=\"text-danger\">*</span>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</label><div class=\"input-group\"><input type=\"number\" class=\"form-control\" id=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var14 string |
|||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(field.JSONName + "_value") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 232, Col: 50} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" name=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var15 string |
|||
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(field.JSONName + "_value") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 233, Col: 52} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" value=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var16 string |
|||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%.0f", convertSecondsToDisplayValue(value, field))) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 234, Col: 91} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\" step=\"1\" min=\"1\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
if field.Required { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, " required") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "> <select class=\"form-select\" id=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var17 string |
|||
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(field.JSONName + "_unit") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 243, Col: 49} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" name=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var18 string |
|||
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(field.JSONName + "_unit") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 244, Col: 51} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\" style=\"max-width: 120px;\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
if field.Required { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, " required") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "><option value=\"minutes\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
if getDisplayUnit(value, field) == "minutes" { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, " selected") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, ">Minutes</option> <option value=\"hours\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
if getDisplayUnit(value, field) == "hours" { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, " selected") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, ">Hours</option> <option value=\"days\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
if getDisplayUnit(value, field) == "days" { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, " selected") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, ">Days</option></select></div>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
if field.Description != "" { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div class=\"form-text text-muted\">") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var19 string |
|||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(field.Description) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 277, Col: 69} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</div>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "</div>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} else if field.InputType == "checkbox" { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "<!-- Checkbox field --> <div class=\"mb-3\"><div class=\"form-check form-switch\"><input class=\"form-check-input\" type=\"checkbox\" id=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var20 string |
|||
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(field.JSONName) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 287, Col: 39} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "\" name=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var21 string |
|||
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(field.JSONName) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 288, Col: 41} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
if getBoolValue(value) { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, " checked") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "> <label class=\"form-check-label\" for=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var22 string |
|||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(field.JSONName) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 293, Col: 68} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "\"><strong>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var23 string |
|||
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(field.DisplayName) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 294, Col: 47} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "</strong></label></div>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
if field.Description != "" { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<div class=\"form-text text-muted\">") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var24 string |
|||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(field.Description) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 298, Col: 69} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</div>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "</div>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} else { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "<!-- Number field --> <div class=\"mb-3\"><label for=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var25 string |
|||
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(field.JSONName) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 304, Col: 39} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "\" class=\"form-label\">") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var26 string |
|||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(field.DisplayName) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 305, Col: 35} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, " ") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
if field.Required { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "<span class=\"text-danger\">*</span>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "</label> <input type=\"number\" class=\"form-control\" id=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var27 string |
|||
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(field.JSONName) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 313, Col: 35} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\" name=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var28 string |
|||
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(field.JSONName) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 314, Col: 37} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\" value=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var29 string |
|||
templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%v", value)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 315, Col: 48} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "\" placeholder=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var30 string |
|||
templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(field.Placeholder) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 316, Col: 47} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
if field.MinValue != nil { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, " min=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var31 string |
|||
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%v", field.MinValue)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 318, Col: 59} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
if field.MaxValue != nil { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, " max=\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var32 string |
|||
templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%v", field.MaxValue)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 321, Col: 59} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "\"") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
if field.Required { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, " required") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "> ") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
if field.Description != "" { |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "<div class=\"form-text text-muted\">") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
var templ_7745c5c3_Var33 string |
|||
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(field.Description) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 328, Col: 69} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "</div>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "</div>") |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
} |
|||
return nil |
|||
}) |
|||
} |
|||
|
|||
// Helper functions for the template
|
|||
func getBoolValue(value interface{}) bool { |
|||
if boolVal, ok := value.(bool); ok { |
|||
return boolVal |
|||
} |
|||
return false |
|||
} |
|||
|
|||
func convertSecondsToDisplayValue(value interface{}, field *maintenance.ConfigField) float64 { |
|||
if intVal, ok := value.(int); ok { |
|||
if intVal == 0 { |
|||
return 0 |
|||
} |
|||
|
|||
// Check if it's evenly divisible by days
|
|||
if intVal%(24*3600) == 0 { |
|||
return float64(intVal / (24 * 3600)) |
|||
} |
|||
|
|||
// Check if it's evenly divisible by hours
|
|||
if intVal%3600 == 0 { |
|||
return float64(intVal / 3600) |
|||
} |
|||
|
|||
// Default to minutes
|
|||
return float64(intVal / 60) |
|||
} |
|||
return 0 |
|||
} |
|||
|
|||
func getDisplayUnit(value interface{}, field *maintenance.ConfigField) string { |
|||
if intVal, ok := value.(int); ok { |
|||
if intVal == 0 { |
|||
return "minutes" |
|||
} |
|||
|
|||
// Check if it's evenly divisible by days
|
|||
if intVal%(24*3600) == 0 { |
|||
return "days" |
|||
} |
|||
|
|||
// Check if it's evenly divisible by hours
|
|||
if intVal%3600 == 0 { |
|||
return "hours" |
|||
} |
|||
|
|||
// Default to minutes
|
|||
return "minutes" |
|||
} |
|||
return "minutes" |
|||
} |
|||
|
|||
// Helper function to convert schema to JSON for JavaScript
|
|||
func schemaToJSON(schema *maintenance.MaintenanceConfigSchema) templ.Component { |
|||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { |
|||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context |
|||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { |
|||
return templ_7745c5c3_CtxErr |
|||
} |
|||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) |
|||
if !templ_7745c5c3_IsBuffer { |
|||
defer func() { |
|||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) |
|||
if templ_7745c5c3_Err == nil { |
|||
templ_7745c5c3_Err = templ_7745c5c3_BufErr |
|||
} |
|||
}() |
|||
} |
|||
ctx = templ.InitializeContext(ctx) |
|||
templ_7745c5c3_Var34 := templ.GetChildren(ctx) |
|||
if templ_7745c5c3_Var34 == nil { |
|||
templ_7745c5c3_Var34 = templ.NopComponent |
|||
} |
|||
ctx = templ.ClearChildren(ctx) |
|||
var templ_7745c5c3_Var35 string |
|||
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(`{}`) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/maintenance_config_schema.templ`, Line: 388, Col: 9} |
|||
} |
|||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35)) |
|||
if templ_7745c5c3_Err != nil { |
|||
return templ_7745c5c3_Err |
|||
} |
|||
return nil |
|||
}) |
|||
} |
|||
|
|||
var _ = templruntime.GeneratedTemplate |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue