Skip to content

Commit

Permalink
feat: add lockfile confirm
Browse files Browse the repository at this point in the history
  • Loading branch information
icai committed Nov 28, 2024
1 parent faf3b18 commit afc3f08
Show file tree
Hide file tree
Showing 12 changed files with 354 additions and 75 deletions.
70 changes: 45 additions & 25 deletions src/components/InstallationWizard.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<template>
<div class="min-h-screen bg-gray-100 flex flex-col items-center justify-center p-6">
<Notification v-bind="notifyData" />
<div class="w-full max-w-2xl bg-white rounded-lg shadow-xl overflow-hidden">
<div class="p-8">
<!-- Layout with Setup Guide on the Top Side -->
Expand Down Expand Up @@ -106,10 +105,6 @@
class="px-4 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-500">
{{ t('wizard.prev') }}
</button>
<!-- <button v-if="currentStep === 0" :disabled="!isLicenseAccepted" @click="nextStep" type="button"
class="px-4 py-2 w-full bg-blue-500 text-white rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-300">
{{ t('wizard.next') }}
</button> -->
<button v-if="currentStep === 0" :class="{
'px-4 py-2 w-full bg-blue-500 text-white rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500': true,
'bg-gray-300 opacity-50': !isLicenseAccepted
Expand Down Expand Up @@ -157,12 +152,14 @@
import { ref, reactive, computed, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { CheckCircleIcon, XCircleIcon } from 'lucide-vue-next'
import Notification from './Notification.vue'
import StepSection from './StepSection.vue'
import StepIndicator from './StepIndicator.vue'
import { languageLabels, defaultConfig, stepsConfig } from '../config'
import { postData, getData } from '../utils/request'
import { Step, Config } from '../types'
import { languageLabels, defaultConfig, stepsConfig } from '@/config'
import { postData, getData } from '@/utils/request'
import { confirm } from '@/utils/confirm'
import { notify } from '@/utils/notify'
import { Step, Config } from '@/types'
import wizardConfig from '../../wizard.config'
const { t, messages, locale, availableLocales } = useI18n()
// Reactive state for license acceptance
Expand Down Expand Up @@ -194,12 +191,6 @@ const stepTexts = [
const steps: Step[] = stepsConfig
const notifyData = reactive({
message: '',
type: 'info',
duration: 3000,
})
// fetch system overview data
const fetchSystemOverview = async () => {
Expand All @@ -212,29 +203,58 @@ const fetchSystemOverview = async () => {
}
onMounted(() => {
notify('Welcome to the installation wizard', 'info')
notify({
message: 'Welcome to the installation wizard',
})
fetchSystemOverview()
})
const notify = (message: string, type: string = 'info') => {
notifyData.message = message
notifyData.type = type
setTimeout(() => {
notifyData.message = ''
}, notifyData.duration)
}
// Modify config dynamically in nextStep
const nextStep = async (event: Event) => {
event.preventDefault()
console.log('nextStep')
if (currentStep.value === 0) {
// License step - check if the user accepted the license
if (!isLicenseAccepted.value) {
notify(t('wizard.licenseError'), 'error') // Handle license acceptance error
notify({
message: t('wizard.licenseError'),
type: 'error'
}) // Handle license acceptance error
return
} else {
// check /data/install.lock file
try {
const res = await getData('/api/status')
if (res.success && res.data.locked) {
if (wizardConfig.canIgnoreLock) {
currentStep.value++
} else {
confirm({
title: t('wizard.installLocked'),
message: t('wizard.installLockedMessage'),
confirmText: t('wizard.installLockedConfirm'),
cancelText: t('wizard.installLockedCancel'),
type: 'info'
}).then((value) => {
if (value) {
currentStep.value++
}
})
}
return
} else {
currentStep.value++
}
} catch (error: Error | any) {
notify({
message: t('wizard.error', { msg: error.message }),
type: 'error'
})
return
}
}
return
}
if (currentStep.value < steps.length - 1) {
currentStep.value++
Expand Down
113 changes: 113 additions & 0 deletions src/components/MsgConfirm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<template>
<div
v-if="visible"
class="fixed inset-0 flex items-center justify-center bg-gray-800 bg-opacity-50 z-50"
>
<div
class="bg-white rounded-lg shadow-lg p-6 w-80"
:class="typeClass"
>
<div class="flex items-center justify-between">
<h3 :class="typeClass" class="text-lg font-semibold flex items-center">
<component :is="typeIcon" class="inline-block mr-2 w-5 h-5" />
{{ title }}
</h3>
<button @click="handleCancel" class="text-gray-500 hover:text-gray-800">
<X class="w-5 h-5" />
</button>
</div>
<div class="mt-4 text-gray-700">{{ message }}</div>
<div class="flex justify-end mt-6 space-x-3">
<button
@click="handleCancel"
class="px-4 py-2 bg-gray-200 rounded-md hover:bg-gray-300 text-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-500"
>
{{ cancelText }}
</button>
<button
@click="handleConfirm"
class="px-4 py-2 rounded-md text-white focus:outline-none focus:ring-2"
:class="confirmButtonClass"
>
{{ confirmText }}
</button>
</div>
</div>
</div>
</template>

<script setup lang="ts">
import { computed, FunctionalComponent } from "vue";
import { Icon, Info, AlertTriangle, AlertCircle, CheckCircle, X } from "lucide-vue-next";
import { defineProps, defineEmits } from "vue";
// Define Props with TypeScript types
const props = defineProps<{
visible: boolean;
title: string;
message: string;
confirmText?: string;
cancelText?: string;
type?: "info" | "error" | "warning" | "success";
}>();
// Define Emits with TypeScript types
const emit = defineEmits<{
(event: "confirm"): void;
(event: "cancel"): void;
}>();
// Default prop values
const confirmText = computed(() => props.confirmText || "OK");
const cancelText = computed(() => props.cancelText || "Cancel");
const type = computed(() => props.type || "info");
// Maps for dynamic properties
const typeIcons: Record<string, FunctionalComponent> = {
info: Info,
error: AlertCircle,
warning: AlertTriangle,
success: CheckCircle,
};
const typeClasses: Record<string, string> = {
info: "text-blue-500",
error: "text-red-500",
warning: "text-yellow-500",
success: "text-green-500",
};
const confirmButtonClasses: Record<string, string> = {
info: "bg-blue-500 hover:bg-blue-600 focus:ring-blue-500",
error: "bg-red-500 hover:bg-red-600 focus:ring-red-500",
warning: "bg-yellow-500 hover:bg-yellow-600 focus:ring-yellow-500",
success: "bg-green-500 hover:bg-green-600 focus:ring-green-500",
};
// Computed properties for dynamic values
const typeIcon = computed(() => typeIcons[type.value] || "info");
const typeClass = computed(() => typeClasses[type.value] || "text-blue-500 bg-blue-100");
const confirmButtonClass = computed(
() => confirmButtonClasses[type.value] || "bg-blue-500"
);
// Event handlers
const handleConfirm = () => emit("confirm");
const handleCancel = () => emit("cancel");
</script>

<style scoped>
/* Add transition effects for the modal and buttons */
.bg-blue-100, .bg-red-100, .bg-yellow-100, .bg-green-100 {
transition: background-color 0.3s ease;
}
button {
transition: background-color 0.2s ease, transform 0.2s ease;
}
button:active {
transform: translateY(0);
}
</style>
Loading

0 comments on commit afc3f08

Please sign in to comment.