Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions front/assets/css/app-semaphore.css
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,133 @@ input[type="radio"][disabled] {
opacity: 1;
color: #96A0A6;
}
/* TomSelect styling to match form-control exactly */
.ts-control {
-moz-appearance: none;
-webkit-appearance: none;
outline: none;
font-size: 16px;
font-size: 1rem;
line-height: 1.5;
padding: 4px 10px;
padding-right: 24px;
border: 0;
border-radius: 6px;
color: #202b30;
background-color: #fff;
font-family: "Fakt Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
transition: background 0.1s ease, border-color 0.1s ease;
position: relative;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2), inset 0 -1px 1px 0 #e5e8ea;
background-position: right 8px center;
background-repeat: no-repeat;
background-image: url('data:image/svg+xml;utf8,<svg width="6px" height="11px" viewBox="0 0 6 11" xmlns="http://www.w3.org/2000/svg"><path d="M3 0l3 4H0l3-4zm0 11L0 7h6l-3 4z" fill="%233B4148" fill-rule="evenodd"/></svg>');
}

.ts-control:focus,
.ts-control.focus {
outline: none;
box-shadow: 0 0 0 2px #00359f !important;
z-index: 4;
}

.ts-control .ts-placeholder {
opacity: 1;
color: #96a0a6;
}

/* Match disabled state */
.ts-control.disabled,
.ts-control[disabled] {
color: #96a0a6;
background-color: #f7fafb;
cursor: not-allowed;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2);
}

/* Hide TomSelect's default dropdown arrow since we're using the background image */
.ts-control .ts-dropdown-trigger {
display: none;
}

/* Ensure TomSelect dropdown matches form styling */
.ts-dropdown {
border-radius: 6px;
border: 0;
box-shadow: rgba(0, 0, 0, 0.35) 0px 6px 30px 3px,
rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;
font-family: "Fakt Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}

/* Style TomSelect options to match */
.ts-dropdown .ts-option {
padding: 4px 10px;
font-size: 16px;
font-size: 1rem;
}

.ts-dropdown .ts-option.selected {
background-color: #00359f;
color: white;
}

.ts-dropdown .ts-option.active {
background-color: #cedcff;
color: #00359f;
}

/* Ensure single value display matches */
.ts-control.single .ts-control-input {
padding: 0;
margin: 0;
background: none;
border: none;
box-shadow: none;
font-size: 16px;
font-size: 1rem;
color: #202b30;
font-family: "Fakt Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}

/* Ensure input in editing mode maintains proper styling */
.ts-control.single .ts-control-input input {
border: 1px solid #E5E8EA !important;
outline: none !important;
box-shadow: none !important;
background: none !important;
font-size: 16px;
font-size: 1rem;
color: #202B30;
font-family: 'Fakt Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}

/* When TomSelect wrapper is in active states, ensure the .ts-control maintains its border */
.ts-wrapper.dropdown-active .ts-control,
.ts-wrapper.input-active .ts-control,
.ts-wrapper.focus .ts-control {
box-shadow: 0 0 0 1px rgba(0, 0, 0, .2), inset 0 -1px 1px 0 #E5E8EA !important;
border: 0 !important;
background-color: #FFF !important;
border-radius: 6px !important;
}

/* Override focus state when dropdown is active to maintain blue focus border */
.ts-wrapper.focus .ts-control {
box-shadow: 0 0 0 2px #00359f !important;
}

/* Ensure TomSelect control always maintains form-control styling in all states */
.ts-control,
.ts-wrapper.dropdown-active .ts-control,
.ts-wrapper.input-active .ts-control {
box-shadow: 0 0 0 1px rgba(0, 0, 0, .2), inset 0 -1px 1px 0 #E5E8EA !important;
background-color: #FFF !important;
border: 0 !important;
border-radius: 6px !important;
}
textarea {
display: block;
min-height: 50px;
Expand Down
83 changes: 77 additions & 6 deletions front/assets/js/tasks/components/target.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
export default {
init(params) {
return new TargetComponent(params.branch, params.pipeline_file)
return new TargetComponent(params.branch || params.reference_name, params.pipeline_file, params.reference_type || 'branch')
}
}

import { Validator, Rule } from './validation'

class TargetComponent {
constructor(branch, pipelineFile) {
constructor(referenceName, pipelineFile, referenceType = 'branch') {
this.currentReferenceType = referenceType

this.validators = {
branch: new Validator('branch', branch, [
branch: new Validator('branch', referenceName, [
new Rule((n) => n.length < 1, 'cannot be empty')
]),
pipelineFile: new Validator('pipelineFile', pipelineFile, [
Expand All @@ -19,6 +21,10 @@ class TargetComponent {

this.handleFieldChange('branch')
this.handleFieldChange('pipelineFile')
this.handleReferenceTypeChange()
this.updateReferenceLabel()
this.updatePlaceholder()
this.updateReferenceTypeText()
}

isValid() {
Expand All @@ -35,10 +41,75 @@ class TargetComponent {
}

handleFieldChange(fieldName) {
document
.querySelector(`[data-validation-input="${fieldName}"]`)
.addEventListener('input', (event) => {
const element = document.querySelector(`[data-validation-input="${fieldName}"]`)
if (element) {
element.addEventListener('input', (event) => {
this.changeFieldValue(fieldName, event.target.value)
})
}
}

handleReferenceTypeChange() {
const referenceTypeInputs = document.querySelectorAll('[data-validation-input="referenceType"]')
referenceTypeInputs.forEach(input => {
input.addEventListener('change', (event) => {
this.changeReferenceType(event.target.value)
})
})
}

changeReferenceType(referenceType) {
this.currentReferenceType = referenceType
this.updateReferenceLabel()
this.updatePlaceholder()
this.updateReferenceTypeText()
}

updateReferenceLabel() {
const labelElement = document.querySelector('[data-reference-label]')
if (labelElement) {
switch (this.currentReferenceType) {
case 'tag':
labelElement.textContent = 'Tag'
break
case 'pr':
labelElement.textContent = 'Pull Request'
break
default:
labelElement.textContent = 'Branch'
}
}
}

updateReferenceTypeText() {
const textElement = document.querySelector('[data-reference-type-text]')
if (textElement) {
switch (this.currentReferenceType) {
case 'tag':
textElement.textContent = 'tag'
break
case 'pr':
textElement.textContent = 'pull request'
break
default:
textElement.textContent = 'branch'
}
}
}

updatePlaceholder() {
const inputElement = document.querySelector('[data-validation-input="branch"]')
if (inputElement) {
switch (this.currentReferenceType) {
case 'tag':
inputElement.placeholder = 'e.g. v1.0.0'
break
case 'pr':
inputElement.placeholder = 'e.g. 123'
break
default:
inputElement.placeholder = 'e.g. master'
}
}
}
}
3 changes: 2 additions & 1 deletion front/assets/js/tasks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ export class Tasks {

static run() {
return JustRunForm.init({
branch: window.InjectedDataByBackend.Tasks.Branch,
referenceType: window.InjectedDataByBackend.Tasks.ReferenceType,
referenceName: window.InjectedDataByBackend.Tasks.ReferenceName,
pipelineFile: window.InjectedDataByBackend.Tasks.PipelineFile,
parameters: window.InjectedDataByBackend.Tasks.Parameters,
})
Expand Down
55 changes: 44 additions & 11 deletions front/assets/js/tasks/just_run_form.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@

import { Validator, Rule } from "./components/validation"
import { TargetParams } from '../workflow_view/target_params'

export default class JustRunForm {
static init(params) {
return new JustRunForm(params)
}

constructor(params) {
this.branchValidator = new Validator('branch', params.branch, [
const referenceName = params.referenceName || 'Enter a branch or tag name…';
const referenceType = params.referenceType || 'branch';

this.referenceNameValidator = new Validator('referenceName', referenceName, [
new Rule((v) => v.length < 1, 'cannot be empty')
])
this.pipelineFileValidator = new Validator('pipelineFile', params.pipelineFile, [
Expand All @@ -17,30 +21,55 @@ export default class JustRunForm {
new Rule((v) => parameter.required && (!v || v.length < 1), 'cannot be empty')
])]))

this.handleBranchChange()
this.currentReferenceType = referenceType
this.handleReferenceTypeChange()
this.handleReferenceNameChange()
this.handlePipelineFileChange()
this.handleParameterChanges()
this.handleSubmitButton()
this.updateReferenceLabel()
this.initializeParameterSelects()
}

renderAll() {
this.branchValidator.render()
this.referenceNameValidator.render()
this.pipelineFileValidator.render()
this.parameterValidators.forEach(
pV => pV.render()
)
}

handleBranchChange() {
const inputField = document.querySelector('[data-validation-input="branch"]')
handleReferenceTypeChange() {
const referenceTypeInputs = document.querySelectorAll('[data-validation-input="referenceType"]')
referenceTypeInputs.forEach(input => {
input.addEventListener('change', (event) => {
this.changeReferenceType(event.target.value)
})
})
}

changeReferenceType(referenceType) {
this.currentReferenceType = referenceType
this.updateReferenceLabel()
}

updateReferenceLabel() {
const labelElement = document.querySelector('[data-reference-label]')
if (labelElement) {
labelElement.textContent = this.currentReferenceType === 'tag' ? 'Tag' : 'Branch'
}
}

handleReferenceNameChange() {
const inputField = document.querySelector('[data-validation-input="referenceName"]')
if (inputField) {
inputField.addEventListener('input', (event) => { this.changeBranchValue(event.target.value) })
inputField.addEventListener('input', (event) => { this.changeReferenceNameValue(event.target.value) })
}
}

changeBranchValue(branchValue) {
this.branchValidator.setValue(branchValue)
this.branchValidator.render()
changeReferenceNameValue(referenceNameValue) {
this.referenceNameValidator.setValue(referenceNameValue)
this.referenceNameValidator.render()
}

handlePipelineFileChange() {
Expand Down Expand Up @@ -75,7 +104,7 @@ export default class JustRunForm {
validateForm() {
const parameterValidators = Array.from(this.parameterValidators.values())

return this.branchValidator.isValid() &&
return this.referenceNameValidator.isValid() &&
this.pipelineFileValidator.isValid() &&
parameterValidators.every(parameterValidator => parameterValidator.isValid())
}
Expand All @@ -84,12 +113,16 @@ export default class JustRunForm {
const submitButton = document.querySelector('[data-action="submit-form"]')
if (!submitButton) { return; }

submitButton.addEventListener('click', (event) => {
submitButton.addEventListener('click', () => {
if (this.validateForm()) {
document.forms[0].submit()
} else {
this.renderAll()
}
})
}

initializeParameterSelects() {
TargetParams.init('[data-parameter-select]')
}
}
2 changes: 1 addition & 1 deletion front/assets/js/workflow_view/switch.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export var Switch = {
$("body").on("click", "[promote-button]", function(event) {
Pollman.stop();

TargetParams.init();
TargetParams.init('[data-promotion-param-name]');
let button = $(event.currentTarget);
let switchId = Switch.parentSwitch(button);
let promotionTarget = Switch.parentPromotionTarget(button);
Expand Down
6 changes: 3 additions & 3 deletions front/assets/js/workflow_view/target_params.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import TomSelect from 'tom-select'

export var TargetParams = {
init: function () {
document.querySelectorAll('[data-promotion-param-name]').forEach((element) => {
init: function (selector = null) {
document.querySelectorAll(selector).forEach((element) => {
if (!element.tomselect) {
new TomSelect(element, {
hidePlaceholder: true,
plugins: ['no_backspace_delete', 'dropdown_input'],
plugins: ['no_backspace_delete'],
})
}
})
Expand Down
Loading