Skip to content
Open
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
16 changes: 11 additions & 5 deletions istsos4-wizard/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import React from 'react';
import { WizardProvider, StepRenderer } from './components/wizard';
import { WizardProvider } from './components/wizard';
import StepRenderer from './components/wizard/StepRenderer';
import ProgressBar from './components/common/ProgressBar';
import Navigation from './components/common/Navigation';
import SessionRecovery from './components/common/SessionRecovery';

function App() {
return (
<WizardProvider>
<div className="min-h-screen bg-gray-50 py-8">
<div className="max-w-4xl mx-auto px-4">
<div className="bg-white rounded-lg shadow-lg p-6">
<div className="min-h-screen bg-gray-50">
<SessionRecovery />
<div className="max-w-4xl mx-auto py-8 px-4">
<div className="bg-white rounded-lg shadow-lg p-8">
<h1 className="text-3xl font-bold text-center mb-8">
IstSOS4 Configuration Wizard
</h1>
<ProgressBar />
<StepRenderer />
<Navigation />
Expand All @@ -19,4 +25,4 @@ function App() {
);
}

export default App;
export default App;
8 changes: 1 addition & 7 deletions istsos4-wizard/src/components/common/FormField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { AlertTriangle, Info } from 'lucide-react';
import { useWizard } from '../../hooks/useWizard';

function FormField({ label, children, error, info, fieldName, required = false }) {
function FormField({ label, children, error, fieldName, required = false }) {
const { state } = useWizard();
const showError = error && state.validation.touched[fieldName];

Expand All @@ -11,12 +11,6 @@ function FormField({ label, children, error, info, fieldName, required = false }
<label className="block text-sm font-medium text-gray-700">
{label}
{required && <span className="text-red-500 ml-1">*</span>}
{info && (
<Info
className="inline w-4 h-4 ml-1 text-gray-400 cursor-help"
title={info}
/>
)}
</label>
{React.cloneElement(children, {
className: `${children.props.className} ${
Expand Down
127 changes: 96 additions & 31 deletions istsos4-wizard/src/components/common/Navigation.jsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,56 @@
import React from 'react';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import { useWizard } from '../../hooks/useWizard';
import React, { useState } from "react";
import { ChevronLeft, ChevronRight, RotateCcw } from "lucide-react";
import { useWizard } from "../../hooks/useWizard";
import { useWizardPersistence } from "../../hooks/useWizardPersistence";

const steps = [
{ title: 'Welcome' },
{ title: 'Server Config' },
{ title: 'Database' },
{ title: 'Data Management' },
{ title: 'Sample Data' },
{ title: 'Performance' },
{ title: 'Services' },
{ title: 'Review' },
{ title: 'Complete' }
{ title: "Welcome" },
{ title: "Server Config" },
{ title: "Database" },
{ title: "Authorization" },
{ title: "Data Management" },
{ title: "Sample Data" },
{ title: "Performance" },
{ title: "Services" },
{ title: "Review" },
{ title: "Complete" },
];

function Navigation() {
const { state, dispatch } = useWizard();
const { resetWizard } = useWizardPersistence();
const [showResetConfirm, setShowResetConfirm] = useState(false);
const [showResetWarning, setShowResetWarning] = useState(false);

const hasErrors = Object.keys(state.validation.errors).length > 0;
const hasTouchedErrors = Object.keys(state.validation.errors).some(
field => state.validation.touched[field]
(field) => state.validation.touched[field]
);

const canGoNext = state.currentStep < state.totalSteps;
const canGoPrev = state.currentStep > 1;

const handleNext = () => {
dispatch({ type: 'NEXT_STEP' });
dispatch({ type: "NEXT_STEP" });
};

const handlePrev = () => {
dispatch({ type: 'PREV_STEP' });
dispatch({ type: "PREV_STEP" });
};

const handleReset = () => {
if (showResetConfirm) {
resetWizard();
setShowResetConfirm(false);
setShowResetWarning(false);
} else {
setShowResetWarning(true);
setShowResetConfirm(true);
setTimeout(() => {
setShowResetConfirm(false);
setShowResetWarning(false);
}, 5000);
}
};

return (
Expand All @@ -38,9 +59,9 @@ function Navigation() {
onClick={handlePrev}
disabled={!canGoPrev}
className={`flex items-center px-4 py-2 rounded-md transition-colors ${
canGoPrev
? 'bg-gray-200 hover:bg-gray-300 text-gray-700'
: 'bg-gray-100 text-gray-400 cursor-not-allowed'
canGoPrev
? "bg-gray-200 hover:bg-gray-300 text-gray-700"
: "bg-gray-100 text-gray-400 cursor-not-allowed"
}`}
>
<ChevronLeft className="w-4 h-4 mr-1" />
Expand All @@ -58,20 +79,64 @@ function Navigation() {
)}
</div>

<button
onClick={handleNext}
disabled={!canGoNext}
className={`flex items-center px-4 py-2 rounded-md transition-colors ${
canGoNext
? 'bg-blue-600 hover:bg-blue-700 text-white'
: 'bg-gray-100 text-gray-400 cursor-not-allowed'
}`}
>
Next
<ChevronRight className="w-4 h-4 ml-1" />
</button>
<div className="flex items-center space-x-2">
<div className="relative">
<button
onClick={handleReset}
className={`p-2 rounded-md transition-colors ${
showResetConfirm
? "bg-red-600 text-white"
: "bg-gray-100 hover:bg-gray-200 text-gray-600"
}`}
>
<RotateCcw className="w-4 h-4" />
</button>

{showResetWarning && showResetConfirm && (
<div className="absolute z-10 w-64 p-3 mb-2 text-sm text-white bg-red-600 rounded-md shadow-lg bottom-full left-1/2 transform -translate-x-1/2">
<div className="flex items-start">
<svg
className="w-4 h-4 mr-2 mt-0.5 flex-shrink-0"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
clipRule="evenodd"
/>
</svg>
<div>
<p className="font-semibold">Warning!</p>
<p className="mt-1">
This will permanently delete all your configuration data and
restart the wizard from the beginning.
</p>
<p className="mt-2 text-xs font-semibold">
Click again to confirm reset
</p>
</div>
</div>
<div className="absolute w-2 h-2 bg-red-600 rotate-45 top-full left-1/2 transform -translate-x-1/2 -mt-1"></div>
</div>
)}
</div>

<button
onClick={handleNext}
disabled={!canGoNext}
className={`flex items-center px-4 py-2 rounded-md transition-colors ${
canGoNext
? "bg-blue-600 hover:bg-blue-700 text-white"
: "bg-gray-100 text-gray-400 cursor-not-allowed"
}`}
>
Next
<ChevronRight className="w-4 h-4 ml-1" />
</button>
</div>
</div>
);
}

export default Navigation;
export default Navigation;
43 changes: 43 additions & 0 deletions istsos4-wizard/src/components/common/SessionRecovery.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { useState, useEffect } from 'react';
import { AlertCircle, X } from 'lucide-react';
import { useWizard } from '../../hooks/useWizard';

function SessionRecovery() {
const { state } = useWizard();
const [showNotification, setShowNotification] = useState(false);

useEffect(() => {

const savedState = localStorage.getItem('istsos4-wizard-state');
if (savedState && state.currentStep > 1) {
setShowNotification(true);

const timer = setTimeout(() => setShowNotification(false), 5000);
return () => clearTimeout(timer);
}
}, []);

if (!showNotification) return null;

return (
<div className="fixed top-4 right-4 max-w-sm bg-blue-50 border border-blue-200 rounded-lg p-4 shadow-lg z-50">
<div className="flex items-start">
<AlertCircle className="w-5 h-5 text-blue-600 mt-0.5 mr-3 flex-shrink-0" />
<div className="flex-1">
<h3 className="font-semibold text-blue-900">Session Recovered</h3>
<p className="text-sm text-blue-800 mt-1">
Your previous progress has been restored. You're on step {state.currentStep} of {state.totalSteps}.
</p>
</div>
<button
onClick={() => setShowNotification(false)}
className="ml-3 text-blue-600 hover:text-blue-800"
>
<X className="w-4 h-4" />
</button>
</div>
</div>
);
}

export default SessionRecovery;
Loading