summary history files

frontend/src/components/PersistentVolumesCreateGuided.vue
<!-- VolumesCreateGuided.vue -->
<script lang="ts">
import { defineComponent, ref, computed } from 'vue';
import { SecretCreateOptions } from '../types/custom'

export default defineComponent({
  name: 'VolumesCreateGuided',

  props: {
    show: {
      type: Boolean,
      required: true
    },
    cluster: {
      type: Object as PropType<KubernetesCluster | null>,
      required: false,
      default: null
    }
  },

  emits: ['close', 'create-volume'],

  setup(props, { emit }) {
    const name = ref('');
    const namespace = ref('default');
    const isSubmitting = ref(false);
    const error = ref('');
    const volumeData = ref([{ key: '', value: '', showValue: false }]);
    const accessModes = ref(['ReadWriteConce']);

    const hostPath = ref({ path: '', type: ''});
    const nfs = ref({ server: '', path: '', readOnly: false });
    const volumeType = ref('');
    const volumeTypes = [
      {
        value: 'hostPath',
        label: 'HostPath',
        category: 'local',
        description: 'Mount a file or directory from the host name (development only)',
      },
      {
        value: 'nfs',
        label: 'NFS',
        category: 'network',
        description: 'Network File System for shared storage across multiple nodes',
      },
    ];

    const isFormValid = computed(() => {
      if (!name.value.trim()) return false;
      if (!capacity.value || capacity.value <= 0) return false;
      if (accessModes.value.length === 0)return false;
      if (!volumeType.value) return false;

      if (volumeType.value === 'hostPath') {
        if (!hostPath.value.path.trim()) {
          return false;
        }
      }

      return true;
    });

    const getVolumeTypeLabel = () => {
      const type = volumeTypes.find(t => t.value === volumeType.value);
      return type ? type.label : '';
    };

    const selectVolumeType = (type: string) => {
      volumeType.value = type;
      error.value = '';
    };

    const addSecretData = () => {
      volumeData.value.push({ key: '', value: '', showValue: false });
    };

    const removeSecretData = (index: number) => {
      if (volumeData.value.length > 1) {
        volumeData.value.splice(index, 1);
      }
    };

    const resetForm = () => {
      name.value = '';
      namespace.value = 'default';
      volumeData.value = [{ key: '', value: '' }];
      error.value = '';
      isSubmitting.value = false;
    };

    const closeModal = () => {
      resetForm();
      emit('close');
    };

    const toggleValueVisibility = (index: number) => {
      volumeData.value[index].showValue = !volumeData.value[index].showValue;
    };

    const createPersistentVolume = () => {
      if (!props.cluster) {
        error.value = 'No cluster selected';
        return;
      }

      if (!isFormValid.value) {
        error.value = 'Please fill in all required fields';
        return;
      }

      isSubmitting.value = true;

      let volumeSource = {};
      switch (volumeType.value) {
        case 'hostPath':
          volumeSource = {
            hostPath: {
              path: hostPath.value.path.trim(),
              ...(hostPath.value.type && { type: hostPath.value.type })
            }
          };
          break;
      }

      const spec: any = {
        capacity: {
          storage: `${capacity.value}${capacityUnit.value}`
        },
        accessModes: accessModes.value,
        persistentVolumeReclaimPolicy: reclaimPolicy.value,
        ...volumeSource
      };

      const stringData = {};
      validData.forEach(item => {
        stringData[item.key.trim()] = item.value;
      });

      const opts: SecretCreateOptions = {
        context: props.cluster.contextName,
        opts: {
          apiVersion: 'v1',
          kind: 'Secret',
          metadata: {
            name: name.value.trim(),
            namespace: namespace.value.trim()
          },
          type: 'Opaque',
          stringData: stringData
        }
      }
      emit('create-volume', opts);
      resetForm();
      closeModal();
    };

    return {
      name,
      namespace,
      volumeData,
      isSubmitting,
      error,
      isFormValid,
      addSecretData,
      removeSecretData,
      closeModal,
      createSecret,
      toggleValueVisibility,
      getVolumeTypeLabel,
      selectVolumeType,


      volumeType,
      volumeTypes,
      hostPath,
      nfs,
      accessModes,

    };
  }
});
</script>

<template>
  <div v-if="show" class="modal-overlay" @click="closeModal">
    <div class="modal-content" @click.stop>
      <div class="modal-header">
        <h3>Create Persistent Volume</h3>
        <button class="close-button" @click="closeModal">&times;</button>
      </div>
      <div class="modal-body">
        <div v-if="error" class="error-message">{{ error }}</div>
        <div class="form-group">
          <label for="name">Name:</label>
          <input 
            id="name" 
            v-model="name" 
            type="text" 
            placeholder="my-persistent-volume"
            :disabled="isSubmitting"
          />
        </div>
        <div class="form-group">
          <label for="storageClass">Storage Class:</label>
          <input 
            id="storageClass" 
            v-model="storageClass" 
            type="text" 
            placeholder="standard"
            :disabled="isSubmitting"
          />
        </div>
        <div class="form-group">
          <label for="capacity">Capacity (Gi):</label>
          <div class="storage-input-group">
            <input 
              id="capacity" 
              v-model="capacity" 
              type="number" 
              min="1"
              placeholder="10"
              :disabled="isSubmitting"
            />
          </div>
        </div>
        <div class="form-section">
          <h4>Volume Type</h4>
          <div class="volume-type-selector">
            <div
              v-for="type in volumeTypes"
              :key="type.value"
              class="volume-type-option"
              :class="{ selected: volumeType === type.value }"
              @click="selectVolumeType(type.value)"
              >
                <div class="type-header">
                  <span class="type-name">{{ type.label }}</span>
                  <span class="type-badge" :class="type.category">{{ type.category }}</span>
                </div>
                <p class="type-description">{{ type.description }}</p>
            </div>
          </div>
        </div>
        <div v-if="volumeType" class="form-section">
          <h4>{{ getVolumeTypeLabel() }} Configuration</h4>
          <div v-if="volumeType == 'hostPath'" class="volume-config">
            <div class="form-group">
              <label for="hostPath">Host Path:</label>
              <input 
                id="hostPath" 
                v-model="hostPath.path" 
                type="text" 
                placeholder="/mnt/data"
                :disabled="isSubmitting"
              />
              <small class="field-hint">Path on the host node</small>
            </div>
            <div class="form-group">
              <label for="hostPathType">Path Type:</label>
              <select id="hostPathType" v-model="hostPath.type" :disabled="isSubmitting">
                <option value="">Default</option>
                <option value="DirectoryOrCreate">DirectoryOrCreate</option>
                <option value="Directory">Directory</option>
                <option value="FileOrCreate">FileOrCreate</option>
                <option value="File">File</option>
                <option value="Socket">Socket</option>
                <option value="CharDevice">CharDevice</option>
                <option value="BlockDevice">BlockDevice</option>
              </select>
            </div>
          </div>
        </div>
        <div class="modal-footer">
          <button 
            class="cancel-button" 
            @click="closeModal"
            :disabled="isSubmitting"
          >
            Cancel
          </button>
          <button 
            class="create-button" 
            @click="createPersistentVolume"
            :disabled="!isFormValid || isSubmitting"
          >
            {{ isSubmitting ? 'Creating...' : 'Create' }}
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<style src="@/assets/css/CreateResource.css" scoped></style>
<style src="@/assets/css/PersistentVolumes.css" scoped></style>