| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 |
- package components
- import "fmt"
- // FormFieldData represents common form field data
- type FormFieldData struct {
- Name string
- Label string
- Description string
- Required bool
- }
- // TextFieldData represents text input field data
- type TextFieldData struct {
- FormFieldData
- Value string
- Placeholder string
- }
- // NumberFieldData represents number input field data
- type NumberFieldData struct {
- FormFieldData
- Value float64
- Step string
- Min *float64
- Max *float64
- }
- // CheckboxFieldData represents checkbox field data
- type CheckboxFieldData struct {
- FormFieldData
- Checked bool
- }
- // SelectFieldData represents select field data
- type SelectFieldData struct {
- FormFieldData
- Value string
- Options []SelectOption
- }
- type SelectOption struct {
- Value string
- Label string
- }
- // DurationFieldData represents duration input field data
- type DurationFieldData struct {
- FormFieldData
- Value string
- Placeholder string
- }
- // DurationInputFieldData represents duration input with number + unit dropdown
- type DurationInputFieldData struct {
- FormFieldData
- Seconds int // The duration value in seconds
- }
- // TextField renders a Bootstrap text input field
- templ TextField(data TextFieldData) {
- <div class="mb-3">
- <label for={ data.Name } class="form-label">
- { data.Label }
- if data.Required {
- <span class="text-danger">*</span>
- }
- </label>
- <input
- type="text"
- class="form-control"
- id={ data.Name }
- name={ data.Name }
- value={ data.Value }
- if data.Placeholder != "" {
- placeholder={ data.Placeholder }
- }
- if data.Required {
- required
- }
- />
- if data.Description != "" {
- <div class="form-text text-muted">{ data.Description }</div>
- }
- </div>
- }
- // NumberField renders a Bootstrap number input field
- templ NumberField(data NumberFieldData) {
- <div class="mb-3">
- <label for={ data.Name } class="form-label">
- { data.Label }
- if data.Required {
- <span class="text-danger">*</span>
- }
- </label>
- <input
- type="number"
- class="form-control"
- id={ data.Name }
- name={ data.Name }
- value={ fmt.Sprintf("%.6g", data.Value) }
- if data.Step != "" {
- step={ data.Step }
- } else {
- step="any"
- }
- if data.Min != nil {
- min={ fmt.Sprintf("%.6g", *data.Min) }
- }
- if data.Max != nil {
- max={ fmt.Sprintf("%.6g", *data.Max) }
- }
- if data.Required {
- required
- }
- />
- if data.Description != "" {
- <div class="form-text text-muted">{ data.Description }</div>
- }
- </div>
- }
- // CheckboxField renders a Bootstrap checkbox field
- templ CheckboxField(data CheckboxFieldData) {
- <div class="mb-3">
- <div class="form-check">
- <input
- type="checkbox"
- class="form-check-input"
- id={ data.Name }
- name={ data.Name }
- if data.Checked {
- checked
- }
- />
- <label class="form-check-label" for={ data.Name }>
- { data.Label }
- </label>
- </div>
- if data.Description != "" {
- <div class="form-text text-muted">{ data.Description }</div>
- }
- </div>
- }
- // SelectField renders a Bootstrap select field
- templ SelectField(data SelectFieldData) {
- <div class="mb-3">
- <label for={ data.Name } class="form-label">
- { data.Label }
- if data.Required {
- <span class="text-danger">*</span>
- }
- </label>
- <select
- class="form-select"
- id={ data.Name }
- name={ data.Name }
- if data.Required {
- required
- }
- >
- for _, option := range data.Options {
- <option
- value={ option.Value }
- if option.Value == data.Value {
- selected
- }
- >
- { option.Label }
- </option>
- }
- </select>
- if data.Description != "" {
- <div class="form-text text-muted">{ data.Description }</div>
- }
- </div>
- }
- // DurationField renders a Bootstrap duration input field
- templ DurationField(data DurationFieldData) {
- <div class="mb-3">
- <label for={ data.Name } class="form-label">
- { data.Label }
- if data.Required {
- <span class="text-danger">*</span>
- }
- </label>
- <input
- type="text"
- class="form-control"
- id={ data.Name }
- name={ data.Name }
- value={ data.Value }
- if data.Placeholder != "" {
- placeholder={ data.Placeholder }
- } else {
- placeholder="e.g., 30m, 2h, 24h"
- }
- if data.Required {
- required
- }
- />
- if data.Description != "" {
- <div class="form-text text-muted">{ data.Description }</div>
- }
- </div>
- }
- // DurationInputField renders a Bootstrap duration input with number + unit dropdown
- templ DurationInputField(data DurationInputFieldData) {
- <div class="mb-3">
- <label for={ data.Name } class="form-label">
- { data.Label }
- if data.Required {
- <span class="text-danger">*</span>
- }
- </label>
- <div class="input-group">
- <input
- type="number"
- class="form-control"
- id={ data.Name }
- name={ data.Name }
- value={ fmt.Sprintf("%.0f", convertSecondsToValue(data.Seconds, convertSecondsToUnit(data.Seconds))) }
- step="1"
- min="1"
- if data.Required {
- required
- }
- />
- <select
- class="form-select"
- id={ data.Name + "_unit" }
- name={ data.Name + "_unit" }
- style="max-width: 120px;"
- >
- <option
- value="minutes"
- if convertSecondsToUnit(data.Seconds) == "minutes" {
- selected
- }
- >
- Minutes
- </option>
- <option
- value="hours"
- if convertSecondsToUnit(data.Seconds) == "hours" {
- selected
- }
- >
- Hours
- </option>
- <option
- value="days"
- if convertSecondsToUnit(data.Seconds) == "days" {
- selected
- }
- >
- Days
- </option>
- </select>
- </div>
- if data.Description != "" {
- <div class="form-text text-muted">{ data.Description }</div>
- }
- </div>
- }
- // Helper functions for duration conversion (used by DurationInputField)
- // Typed conversion functions for protobuf int32 (most common) - EXPORTED
- func ConvertInt32SecondsToDisplayValue(seconds int32) float64 {
- return convertIntSecondsToDisplayValue(int(seconds))
- }
- func GetInt32DisplayUnit(seconds int32) string {
- return getIntDisplayUnit(int(seconds))
- }
- // Typed conversion functions for regular int
- func convertIntSecondsToDisplayValue(seconds int) float64 {
- if seconds == 0 {
- return 0
- }
-
- // Check if it's evenly divisible by days
- if seconds%(24*3600) == 0 {
- return float64(seconds / (24 * 3600))
- }
-
- // Check if it's evenly divisible by hours
- if seconds%3600 == 0 {
- return float64(seconds / 3600)
- }
-
- // Default to minutes
- return float64(seconds / 60)
- }
- func getIntDisplayUnit(seconds int) string {
- if seconds == 0 {
- return "minutes"
- }
-
- // Check if it's evenly divisible by days
- if seconds%(24*3600) == 0 {
- return "days"
- }
-
- // Check if it's evenly divisible by hours
- if seconds%3600 == 0 {
- return "hours"
- }
-
- // Default to minutes
- return "minutes"
- }
- func convertSecondsToUnit(seconds int) string {
- if seconds == 0 {
- return "minutes"
- }
-
- // Try days first
- if seconds%(24*3600) == 0 && seconds >= 24*3600 {
- return "days"
- }
-
- // Try hours
- if seconds%3600 == 0 && seconds >= 3600 {
- return "hours"
- }
-
- // Default to minutes
- return "minutes"
- }
- func convertSecondsToValue(seconds int, unit string) float64 {
- if seconds == 0 {
- return 0
- }
-
- switch unit {
- case "days":
- return float64(seconds / (24 * 3600))
- case "hours":
- return float64(seconds / 3600)
- case "minutes":
- return float64(seconds / 60)
- default:
- return float64(seconds / 60) // Default to minutes
- }
- }
- // IntervalFieldData represents interval input field data with separate value and unit
- type IntervalFieldData struct {
- FormFieldData
- Seconds int // The interval value in seconds
- }
- // IntervalField renders a Bootstrap interval input with number + unit dropdown (like task config)
- templ IntervalField(data IntervalFieldData) {
- <div class="mb-3">
- <label for={ data.Name } class="form-label">
- { data.Label }
- if data.Required {
- <span class="text-danger">*</span>
- }
- </label>
- <div class="input-group">
- <input
- type="number"
- class="form-control"
- id={ data.Name + "_value" }
- name={ data.Name + "_value" }
- value={ fmt.Sprintf("%.0f", convertSecondsToValue(data.Seconds, convertSecondsToUnit(data.Seconds))) }
- step="1"
- min="1"
- if data.Required {
- required
- }
- />
- <select
- class="form-select"
- id={ data.Name + "_unit" }
- name={ data.Name + "_unit" }
- style="max-width: 120px;"
- if data.Required {
- required
- }
- >
- <option
- value="minutes"
- if convertSecondsToUnit(data.Seconds) == "minutes" {
- selected
- }
- >
- Minutes
- </option>
- <option
- value="hours"
- if convertSecondsToUnit(data.Seconds) == "hours" {
- selected
- }
- >
- Hours
- </option>
- <option
- value="days"
- if convertSecondsToUnit(data.Seconds) == "days" {
- selected
- }
- >
- Days
- </option>
- </select>
- </div>
- if data.Description != "" {
- <div class="form-text text-muted">{ data.Description }</div>
- }
- </div>
- }
|