feat(kube): create namespace from manifest view [EE-3479] (#7306)

Restyle create from manifest
pull/7399/head
Matt Hook 2022-08-01 16:44:56 +12:00 committed by GitHub
parent 11c778cfeb
commit ddaf9dc885
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 154 additions and 90 deletions

View File

@ -79,6 +79,12 @@
background-color: var(--ui-gray-3); background-color: var(--ui-gray-3);
} }
.switch-values {
font-style: normal;
font-weight: 500;
margin-left: 5px;
}
/* Toggle */ /* Toggle */
.slider { .slider {

View File

@ -13,7 +13,7 @@
<uib-tab index="0"> <uib-tab index="0">
<uib-tab-heading> <pr-icon icon="'code'" feather="true"></pr-icon> Deploy </uib-tab-heading> <uib-tab-heading> <pr-icon icon="'code'" feather="true"></pr-icon> Deploy </uib-tab-heading>
<div class="col-sm-12 form-section-title"> Namespace </div> <div class="col-sm-12 form-section-title"> Namespace </div>
<form class="form-horizontal" style="margin-top: 20px" name="deploymentForm"> <form class="form-horizontal mt-3" name="deploymentForm">
<div class="form-group" ng-if="ctrl.formValues.Namespace"> <div class="form-group" ng-if="ctrl.formValues.Namespace">
<label for="target_node" class="col-lg-2 col-sm-3 control-label text-left">Namespace</label> <label for="target_node" class="col-lg-2 col-sm-3 control-label text-left">Namespace</label>
<div class="col-sm-8"> <div class="col-sm-8">
@ -31,16 +31,18 @@
<portainer-tooltip message="'If you have defined namespaces in your deployment file turning this on will enforce the use of those only in the deployment'"> <portainer-tooltip message="'If you have defined namespaces in your deployment file turning this on will enforce the use of those only in the deployment'">
</portainer-tooltip> </portainer-tooltip>
</label> </label>
<div class="col-sm-8"> <div class="col-sm-8 vertical-center pt-3">
<label class="switch"> <label class="switch">
<input type="checkbox" name="toggle_logo" ng-model="ctrl.formValues.namespace_toggle" /> <input type="checkbox" name="toggle_logo" ng-model="ctrl.formValues.namespace_toggle" />
<i></i> <span class="slider round"></span>
</label> </label>
<!-- <span class="ml-2 mb-1 switch-values" ng-if="ctrl.formValues.namespace_toggle">Yes</span>
<span class="ml-2 mb-1 switch-values" ng-if="!ctrl.formValues.namespace_toggle">No</span> -->
</div> </div>
</div> </div>
<div class="form-group" ng-if="!ctrl.formValues.Namespace"> <div class="form-group" ng-if="!ctrl.formValues.Namespace">
<div class="col-sm-12 small text-muted"> <div class="col-sm-12 small text-muted">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px"></i> <pr-icon icon="'alert-triangle'" feather="true" mode="'warning'"></pr-icon>
You do not have access to any namespace. Contact your administrator to get access to a namespace. You do not have access to any namespace. Contact your administrator to get access to a namespace.
</div> </div>
</div> </div>
@ -103,6 +105,7 @@
</div> </div>
<!-- editor --> <!-- editor -->
<div class="mt-4">
<web-editor-form <web-editor-form
ng-if="ctrl.state.BuildMethod === ctrl.BuildMethods.WEB_EDITOR || (ctrl.state.BuildMethod === ctrl.BuildMethods.CUSTOM_TEMPLATE && ctrl.state.templateId)" ng-if="ctrl.state.BuildMethod === ctrl.BuildMethods.WEB_EDITOR || (ctrl.state.BuildMethod === ctrl.BuildMethods.CUSTOM_TEMPLATE && ctrl.state.templateId)"
identifier="kubernetes-deploy-editor" identifier="kubernetes-deploy-editor"
@ -113,8 +116,8 @@
placeholder="# Define or paste the content of your manifest file here" placeholder="# Define or paste the content of your manifest file here"
> >
<editor-description> <editor-description>
<div ng-show="ctrl.state.DeployType === ctrl.ManifestDeployTypes.COMPOSE"> <span class="col-sm-12 text-muted small" ng-show="ctrl.state.DeployType === ctrl.ManifestDeployTypes.COMPOSE">
<p class="vertical-center"> <p>
<pr-icon icon="'alert-circle'" mode="'warning'" feather="true"></pr-icon> <pr-icon icon="'alert-circle'" mode="'warning'" feather="true"></pr-icon>
Portainer uses <a href="https://kompose.io/" target="_blank">Kompose</a> to convert your Compose manifest to a Kubernetes compliant manifest. Be wary that Portainer uses <a href="https://kompose.io/" target="_blank">Kompose</a> to convert your Compose manifest to a Kubernetes compliant manifest. Be wary that
not all the Compose format options are supported by Kompose at the moment. not all the Compose format options are supported by Kompose at the moment.
@ -123,9 +126,9 @@
You can get more information about Compose file format in the You can get more information about Compose file format in the
<a href="https://docs.docker.com/compose/compose-file/" target="_blank">official documentation</a>. <a href="https://docs.docker.com/compose/compose-file/" target="_blank">official documentation</a>.
</p> </p>
</div> </span>
<div ng-show="ctrl.state.DeployType === ctrl.ManifestDeployTypes.KUBERNETES"> <span class="col-sm-12 text-muted small" ng-show="ctrl.state.DeployType === ctrl.ManifestDeployTypes.KUBERNETES">
<p class="vertical-center"> <p>
<pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon> <pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon>
This feature allows you to deploy any kind of Kubernetes resource in this environment (Deployment, Secret, ConfigMap...). This feature allows you to deploy any kind of Kubernetes resource in this environment (Deployment, Secret, ConfigMap...).
</p> </p>
@ -133,10 +136,10 @@
You can get more information about Kubernetes file format in the You can get more information about Kubernetes file format in the
<a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/" target="_blank">official documentation</a>. <a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/" target="_blank">official documentation</a>.
</p> </p>
</div> </span>
</editor-description> </editor-description>
</web-editor-form> </web-editor-form>
</div>
<!-- !editor --> <!-- !editor -->
<!-- url --> <!-- url -->
@ -188,7 +191,7 @@
<uib-tab index="1" disable="ctrl.state.tabLogsDisabled"> <uib-tab index="1" disable="ctrl.state.tabLogsDisabled">
<uib-tab-heading> <pr-icon icon="'file-text'" feather="true"></pr-icon> Logs </uib-tab-heading> <uib-tab-heading> <pr-icon icon="'file-text'" feather="true"></pr-icon> Logs </uib-tab-heading>
<form class="form-horizontal" style="margin-top: 20px"> <form class="form-horizontal mt-3">
<div class="form-group" ng-if="ctrl.state.activeTab === 1"> <div class="form-group" ng-if="ctrl.state.activeTab === 1">
<div class="col-sm-12"> <div class="col-sm-12">
<code-editor identifier="kubernetes-deploy-logs" read-only="true" yml="false" value="ctrl.errorLog"></code-editor> <code-editor identifier="kubernetes-deploy-logs" read-only="true" yml="false" value="ctrl.errorLog"></code-editor>

View File

@ -27,15 +27,15 @@ class KubernetesDeployController {
this.isTemplateVariablesEnabled = isBE; this.isTemplateVariablesEnabled = isBE;
this.deployOptions = [ this.deployOptions = [
buildOption('method_kubernetes', 'fa fa-cubes', 'Kubernetes', 'Kubernetes manifest format', KubernetesDeployManifestTypes.KUBERNETES), buildOption('method_kubernetes', 'svg-kubernetes', 'Kubernetes', 'Kubernetes manifest format', KubernetesDeployManifestTypes.KUBERNETES),
buildOption('method_compose', 'fab fa-docker', 'Compose', 'Docker compose format', KubernetesDeployManifestTypes.COMPOSE), buildOption('method_compose', 'svg-dockercompose', 'Compose', 'Docker compose format', KubernetesDeployManifestTypes.COMPOSE),
]; ];
this.methodOptions = [ this.methodOptions = [
buildOption('method_repo', 'fab fa-github', 'Git Repository', 'Use a git repository', KubernetesDeployBuildMethods.GIT), buildOption('method_repo', 'svg-git', 'Git Repository', 'Use a git repository', KubernetesDeployBuildMethods.GIT),
buildOption('method_editor', 'fa fa-edit', 'Web editor', 'Use our Web editor', KubernetesDeployBuildMethods.WEB_EDITOR), buildOption('method_editor', 'svg-custom', 'Web editor', 'Use our Web editor', KubernetesDeployBuildMethods.WEB_EDITOR),
buildOption('method_url', 'fa fa-globe', 'URL', 'Specify a URL to a file', KubernetesDeployBuildMethods.URL), buildOption('method_url', 'svg-url', 'URL', 'Specify a URL to a file', KubernetesDeployBuildMethods.URL),
buildOption('method_template', 'fa fa-rocket', 'Custom Template', 'Use a custom template', KubernetesDeployBuildMethods.CUSTOM_TEMPLATE), buildOption('method_template', 'svg-template', 'Custom Template', 'Use a custom template', KubernetesDeployBuildMethods.CUSTOM_TEMPLATE),
]; ];
this.state = { this.state = {

View File

@ -1,7 +1,7 @@
<div> <div>
<div class="form-group"> <div class="form-group pt-3">
<label for="stack_template" class="col-sm-1 control-label text-left"> Template </label> <label for="stack_template" class="col-sm-3 col-lg-2 control-label text-left"> Template </label>
<div class="col-sm-11"> <div class="col-sm-8 vertical-center">
<select <select
ng-if="$ctrl.templates.length" ng-if="$ctrl.templates.length"
class="form-control" class="form-control"
@ -11,7 +11,10 @@
> >
<option value="" label="Select a Custom template" disabled selected="selected"> </option> <option value="" label="Select a Custom template" disabled selected="selected"> </option>
</select> </select>
<span ng-if="!$ctrl.templates.length"> No custom templates are available. Head over to the <a ui-state="$ctrl.newTemplatePath">custom template view</a> to create one. </span> <span class="small text-muted pt-[7px]" ng-if="!$ctrl.templates.length">
No custom templates are available. Head over to the <a class="text-blue-8 hover:underline hover:text-blue-8" ui-state="$ctrl.newTemplatePath">custom template view</a> to
create one.
</span>
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
<ng-form class="env-item form-horizontal" name="$ctrl.{{ $ctrl.formName }}"> <ng-form class="env-item form-horizontal" name="$ctrl.{{ $ctrl.formName }}">
<div class="form-group col-sm-12"> <div class="form-group col-sm-12">
<div class="form-inline" style="margin-top: 10px"> <div class="form-inline mt-3">
<div class="input-group col-sm-5 input-group-sm"> <div class="input-group col-sm-5 input-group-sm">
<span class="input-group-addon">path</span> <span class="input-group-addon">path</span>
<input <input
@ -18,10 +18,12 @@
</button> </button>
</div> </div>
<div ng-show="$ctrl[$ctrl.formName].name.$invalid"> <div ng-show="$ctrl[$ctrl.formName].name.$invalid">
<div class="small"> <div class="small text-muted">
<div ng-messages="$ctrl[$ctrl.formName].name.$error" class="mt-1"> <div ng-messages="$ctrl[$ctrl.formName].name.$error" class="mt-1">
<p ng-message="required"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Path is required. </p> <p class="vertical-center" ng-message="required"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Path is required. </p>
<p ng-message="pattern"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> File path must include yaml, yml, json, or hcl extension </p> <p class="vertical-center" ng-message="pattern">
<pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> File path must include yaml, yml, json, or hcl extension
</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,11 +1,15 @@
<div class="form-group"> <div class="form-group">
<div class="col-sm-12" style="margin-top: 5px"> <div class="col-sm-12 p-0">
<div class="col-sm-3 col-lg-2">
<label class="control-label text-left">Additional paths</label> <label class="control-label text-left">Additional paths</label>
<span class="label label-default interactive" style="margin-left: 10px" ng-click="$ctrl.add()"> </div>
<pr-icon icon="'plus'" size="'sm'" mode="'alt'" feather="true"></pr-icon> add file <div class="col-sm-9 pt-1">
<span class="label label-default interactive vertical-center" ng-click="$ctrl.add()">
<pr-icon icon="'plus'" size="'sm'" mode="'alt'" feather="true"></pr-icon> <span>add file</span>
</span> </span>
</div> </div>
<div class="col-sm-12 form-inline" style="margin-top: 10px"> </div>
<div class="col-sm-12 form-inline">
<git-form-additional-file-item <git-form-additional-file-item
ng-repeat="variable in $ctrl.model.AdditionalFiles track by $index" ng-repeat="variable in $ctrl.model.AdditionalFiles track by $index"
variable="variable" variable="variable"

View File

@ -1,12 +1,13 @@
<div class="form-group"> <div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<por-switch-field <por-switch-field
label-class="'col-sm-2'"
checked="$ctrl.model.RepositoryAuthentication" checked="$ctrl.model.RepositoryAuthentication"
label="'Authentication'" label="'Authentication'"
label-class="'col-sm-3 col-lg-2'"
name="'authSwitch'" name="'authSwitch'"
on-change="($ctrl.onChangeAuth)" on-change="($ctrl.onChangeAuth)"
data-cy="'component-gitAuthToggle'" data-cy="'component-gitAuthToggle'"
switch-values="{on:'Yes',off:'No'}"
></por-switch-field> ></por-switch-field>
</div> </div>
</div> </div>

View File

@ -1,14 +1,21 @@
<ng-form name="autoUpdateForm"> <ng-form name="autoUpdateForm">
<div class="form-group"> <div class="form-group">
<div class="col-sm-12"> <div class="col-sm-12">
<por-switch-field name="'autoUpdate'" checked="$ctrl.model.RepositoryAutomaticUpdates" label="'Automatic Updates'" on-change="($ctrl.onChangeAutoUpdate)"></por-switch-field> <por-switch-field
name="'autoUpdate'"
checked="$ctrl.model.RepositoryAutomaticUpdates"
label="'Automatic Updates'"
label-class="'col-sm-3 col-lg-2'"
on-change="($ctrl.onChangeAutoUpdate)"
switch-values="{on:'Yes',off:'No'}"
></por-switch-field>
</div> </div>
</div> </div>
<div class="small text-warning" style="margin: 5px 0 10px 0" ng-if="$ctrl.model.RepositoryAutomaticUpdates"> <div class="small" ng-if="$ctrl.model.RepositoryAutomaticUpdates">
<pr-icon icon="'alert-circle'" mode="'warning'" feather="true"></pr-icon> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon>
<span class="text-muted">Any changes to this stack or application made locally in Portainer will be overridden, which may cause service interruption.</span> <span class="text-muted">Any changes to this stack or application made locally in Portainer will be overridden, which may cause service interruption.</span>
</div> </div>
<div class="form-group" ng-if="$ctrl.model.RepositoryAutomaticUpdates"> <div class="form-group mt-2" ng-if="$ctrl.model.RepositoryAutomaticUpdates">
<label for="repository_mechanism" class="col-lg-2 col-sm-3 control-label text-left"> Mechanism </label> <label for="repository_mechanism" class="col-lg-2 col-sm-3 control-label text-left"> Mechanism </label>
<div class="col-sm-8"> <div class="col-sm-8">
<div class="input-group col-sm-10 input-group-sm"> <div class="input-group col-sm-10 input-group-sm">
@ -49,10 +56,9 @@
required required
interval-format interval-format
/> />
</div> <div class="help-group">
</div> <div class="form-group col-md-12 pt-1" ng-show="autoUpdateForm.repository_fetch_interval.$touched && autoUpdateForm.repository_fetch_interval.$invalid">
<div class="form-group col-md-12" ng-show="autoUpdateForm.repository_fetch_interval.$touched && autoUpdateForm.repository_fetch_interval.$invalid"> <div class="small text-muted">
<div class="small">
<div ng-messages="autoUpdateForm.repository_fetch_interval.$error"> <div ng-messages="autoUpdateForm.repository_fetch_interval.$error">
<p ng-message="required"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> This field is required.</p> <p ng-message="required"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> This field is required.</p>
<p ng-message="invalidIntervalFormat"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Please enter a valid time interval.</p> <p ng-message="invalidIntervalFormat"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Please enter a valid time interval.</p>
@ -60,6 +66,10 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
</div>
<div class="form-group" ng-if="$ctrl.showForcePullImage && $ctrl.model.RepositoryAutomaticUpdates"> <div class="form-group" ng-if="$ctrl.showForcePullImage && $ctrl.model.RepositoryAutomaticUpdates">
<div class="col-sm-12"> <div class="col-sm-12">
<por-switch-field <por-switch-field
@ -67,6 +77,7 @@
feature="$ctrl.limitedFeaturePullImage" feature="$ctrl.limitedFeaturePullImage"
checked="$ctrl.model.ForcePullImage" checked="$ctrl.model.ForcePullImage"
label="'Pull latest image'" label="'Pull latest image'"
label-class="'col-sm-3 col-lg-2'"
on-change="($ctrl.onChangeForcePullImage)" on-change="($ctrl.onChangeForcePullImage)"
></por-switch-field> ></por-switch-field>
</div> </div>
@ -79,16 +90,17 @@
feature-id="$ctrl.limitedFeature" feature-id="$ctrl.limitedFeature"
checked="$ctrl.model.RepositoryAutomaticUpdatesForce" checked="$ctrl.model.RepositoryAutomaticUpdatesForce"
label="'Force Redeployment'" label="'Force Redeployment'"
label-class="'col-sm-3 col-lg-2'"
on-change="($ctrl.onChangeAutoUpdateForce)" on-change="($ctrl.onChangeAutoUpdateForce)"
></por-switch-field> ></por-switch-field>
</div> </div>
</div> </div>
<div class="small" style="margin: 5px 0 10px 0" ng-if="$ctrl.model.RepositoryAutomaticUpdates"> <div class="small" ng-if="$ctrl.model.RepositoryAutomaticUpdates">
<pr-icon icon="'alert-circle'" mode="'primary'" feather="true"></pr-icon> <pr-icon icon="'info'" mode="'primary'" feather="true"></pr-icon>
<span class="text-muted">When enabled, enforces automatic deployment at each interval or webhook invocation.</span> <span class="text-muted">When enabled, enforces automatic deployment at each interval or webhook invocation.</span>
</div> </div>
<div class="small" style="margin: 5px 0 10px 0" ng-if="!$ctrl.model.RepositoryAutomaticUpdates"> <div class="small" ng-if="!$ctrl.model.RepositoryAutomaticUpdates">
<pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> <pr-icon icon="'info'" mode="'primary'" feather="true"></pr-icon>
<span class="text-muted">When enabled, updates from the git repository will occur automatically at an interval or webhook.</span> <span class="text-muted">When enabled, updates from the git repository will occur automatically at an interval or webhook.</span>
</div> </div>
</ng-form> </ng-form>

View File

@ -19,7 +19,9 @@
ng-pattern="/.+\.(yml|yaml|json|hcl)$/i" ng-pattern="/.+\.(yml|yaml|json|hcl)$/i"
required required
/> />
<p class="mt-2" ng-show="pathForm.repoPathField.$error.pattern"> <pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Invalid file path </p> <p class="mt-2 text-muted small vertical-center" ng-show="pathForm.repoPathField.$error.pattern">
<pr-icon icon="'alert-triangle'" mode="'warning'" feather="true"></pr-icon> Invalid file path
</p>
</div> </div>
</div> </div>
</ng-form> </ng-form>

View File

@ -1,6 +1,10 @@
.boxselector_wrapper { .boxselector_wrapper {
display: flex; display: flex;
flex-flow: row wrap; flex-flow: row wrap;
gap: 10px;
overflow: hidden !important;
margin-bottom: 5px;
margin-top: 5px;
} }
@media only screen and (max-width: 700px) { @media only screen and (max-width: 700px) {

View File

@ -2,11 +2,11 @@
.box-selector-item { .box-selector-item {
--selected-item-color: var(--ui-blue-6); --selected-item-color: var(--ui-blue-6);
flex: 1; flex: 1;
padding: 5px;
} }
.boxselector_wrapper .boxselector_header { .boxselector_wrapper .boxselector_header,
font-size: 14px; .box-selector-item .boxselector_header {
font-size: 18px;
margin-bottom: 5px; margin-bottom: 5px;
font-weight: bold; font-weight: bold;
user-select: none; user-select: none;
@ -17,22 +17,26 @@
font-weight: normal; font-weight: normal;
} }
.boxselector_wrapper input[type='radio'] { .boxselector_wrapper input[type='radio'],
.box-selector-item input[type='radio'] {
display: none; display: none;
} }
.boxselector_wrapper input[type='radio']:not(:disabled) ~ label { .boxselector_wrapper input[type='radio']:not(:disabled) ~ label,
.box-selector-item input[type='radio']:not(:disabled) ~ label {
cursor: pointer; cursor: pointer;
background-color: var(--bg-boxselector-wrapper-disabled-color); background-color: var(--bg-boxselector-wrapper-disabled-color);
text-align: left; text-align: left;
height: 100%; height: 100%;
} }
.boxselector_wrapper input[type='radio']:not(:disabled):hover ~ label:hover { .boxselector_wrapper input[type='radio']:not(:disabled):hover ~ label:hover,
.box-selector-item input[type='radio']:not(:disabled):hover ~ label:hover {
cursor: pointer; cursor: pointer;
} }
.boxselector_wrapper label { .boxselector_wrapper label,
.box-selector-item label {
font-weight: normal; font-weight: normal;
font-size: 12px; font-size: 12px;
display: block; display: block;
@ -109,6 +113,7 @@
} }
.box-selector-item.limited.business :checked + label { .box-selector-item.limited.business :checked + label {
background-color: initial;
color: initial; color: initial;
} }
@ -126,11 +131,9 @@
.boxselector_icon, .boxselector_icon,
.boxselector_icon img { .boxselector_icon img {
height: 48px;
width: 48px;
color: var(--ui-blue-8); color: var(--ui-blue-8);
font-size: 48px; font-size: 90px;
margin-top: 20px; display: block;
} }
.boxselector_header pr-icon { .boxselector_header pr-icon {
@ -140,3 +143,17 @@
.boxselector_content { .boxselector_content {
padding-left: 20px; padding-left: 20px;
} }
.boxselector_wrapper input[type='radio']:not(:disabled) ~ label,
.box-selector-item input[type='radio']:not(:disabled) ~ label {
background-color: var(--ui-gray-2);
}
.boxselector_img_container {
line-height: 90px;
margin-bottom: 0;
}
.box-selector-item p {
margin-bottom: 0;
}

View File

@ -7,3 +7,9 @@
.label { .label {
padding: 0; padding: 0;
} }
.switchValues {
font-style: normal;
font-weight: 500;
margin-left: 5px;
}

View File

@ -65,10 +65,14 @@ export function SwitchField({
dataCy={dataCy} dataCy={dataCy}
/> />
{switchValues && checked && ( {switchValues && checked && (
<span className="ml-2">{switchValues.on}</span> <span className={`"ml-2" ${styles.switchValues}`}>
{switchValues.on}
</span>
)} )}
{switchValues && !checked && ( {switchValues && !checked && (
<span className="ml-2">{switchValues.off}</span> <span className={`"ml-2" ${styles.switchValues}`}>
{switchValues.off}
</span>
)} )}
</label> </label>
); );