diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a2544c260..8cd379ff6 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,7 +10,9 @@ "dependencies": { "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-progress": "^1.1.7", + "@radix-ui/react-radio-group": "^1.3.8", "@radix-ui/react-scroll-area": "^1.2.9", "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slider": "^1.3.5", @@ -3609,6 +3611,29 @@ } } }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-menu": { "version": "2.1.15", "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.15.tgz", @@ -3776,6 +3801,99 @@ } } }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz", + "integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-roving-focus": { "version": "1.1.10", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz", diff --git a/frontend/package.json b/frontend/package.json index 677bb9fa4..f528f393c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,7 +25,9 @@ "dependencies": { "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-progress": "^1.1.7", + "@radix-ui/react-radio-group": "^1.3.8", "@radix-ui/react-scroll-area": "^1.2.9", "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slider": "^1.3.5", diff --git a/frontend/public/avatars/avatar1.png b/frontend/public/avatars/avatar1.png new file mode 100644 index 000000000..1d96b66de Binary files /dev/null and b/frontend/public/avatars/avatar1.png differ diff --git a/frontend/public/avatars/avatar2.png b/frontend/public/avatars/avatar2.png new file mode 100644 index 000000000..f4c1d1bb3 Binary files /dev/null and b/frontend/public/avatars/avatar2.png differ diff --git a/frontend/public/avatars/avatar3.png b/frontend/public/avatars/avatar3.png new file mode 100644 index 000000000..99f70dc11 Binary files /dev/null and b/frontend/public/avatars/avatar3.png differ diff --git a/frontend/public/avatars/avatar4.png b/frontend/public/avatars/avatar4.png new file mode 100644 index 000000000..53c4de21e Binary files /dev/null and b/frontend/public/avatars/avatar4.png differ diff --git a/frontend/public/avatars/avatar5.png b/frontend/public/avatars/avatar5.png new file mode 100644 index 000000000..24013cd0c Binary files /dev/null and b/frontend/public/avatars/avatar5.png differ diff --git a/frontend/public/avatars/avatar6.png b/frontend/public/avatars/avatar6.png new file mode 100644 index 000000000..292d909cf Binary files /dev/null and b/frontend/public/avatars/avatar6.png differ diff --git a/frontend/public/avatars/avatar7.png b/frontend/public/avatars/avatar7.png new file mode 100644 index 000000000..9817b0b0e Binary files /dev/null and b/frontend/public/avatars/avatar7.png differ diff --git a/frontend/public/avatars/avatar8.png b/frontend/public/avatars/avatar8.png new file mode 100644 index 000000000..9705fa099 Binary files /dev/null and b/frontend/public/avatars/avatar8.png differ diff --git a/frontend/src/components/OnboardingSteps/AvatarSelectionStep.tsx b/frontend/src/components/OnboardingSteps/AvatarSelectionStep.tsx new file mode 100644 index 000000000..d156534e6 --- /dev/null +++ b/frontend/src/components/OnboardingSteps/AvatarSelectionStep.tsx @@ -0,0 +1,145 @@ +'use client'; + +import React, { useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { RootState } from '@/store'; +import { + setAvatar, + setName, + markCompleted, +} from '../../features/onboardingSlice'; + +import { Button } from '@/components/ui/button'; +import { Card, CardHeader, CardContent } from '@/components/ui/card'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; + +import { AppFeatures } from './AppFeatures'; + +interface AvatarNameSelectionStepProps { + stepIndex: number; + totalSteps: number; +} + +const avatars = [ + '/avatars/avatar1.png', + '/avatars/avatar2.png', + '/avatars/avatar3.png', + '/avatars/avatar4.png', + '/avatars/avatar5.png', + '/avatars/avatar6.png', + '/avatars/avatar7.png', + '/avatars/avatar8.png', +]; + +export const AvatarSelectionStep: React.FC = ({ + stepIndex, + totalSteps, +}) => { + const dispatch = useDispatch(); + const name = useSelector((state: RootState) => state.onboarding.name); + const selectedAvatar = useSelector( + (state: RootState) => state.onboarding.avatar, + ); + + const handleAvatarSelect = (avatar: string) => { + dispatch(setAvatar(avatar)); + }; + + const handleNameChange = (value: string) => { + dispatch(setName(value)); + }; + + const handleNextClick = () => { + if (name && selectedAvatar) { + dispatch(markCompleted(stepIndex)); + } else { + alert('Please enter your name and select an avatar.'); + } + }; + + return ( +
+
+ {/* Left Card */} + +
+ +
+ + Step {stepIndex + 1} of {totalSteps} + + {Math.round(((stepIndex + 1) / totalSteps) * 100)}% +
+
+
+
+ + +

Welcome to PictoPy

+

+ Let's get to know you a little better +

+ + {/* Name Input */} +
+ + handleNameChange(e.target.value)} + className="h-8 text-sm placeholder:text-sm" + /> +
+ + {/* Avatar Grid */} +
+ +
+ {avatars.map((avatar) => { + const isSelected = selectedAvatar === avatar; + return ( + + ); + })} +
+
+ + {/* Next Button */} +
+ +
+
+ + + {/* Right Card using AppFeatures */} + +
+
+ ); +}; diff --git a/frontend/src/components/OnboardingSteps/FolderSetupStep.tsx b/frontend/src/components/OnboardingSteps/FolderSetupStep.tsx index 084c5fb35..c0ae7b49a 100644 --- a/frontend/src/components/OnboardingSteps/FolderSetupStep.tsx +++ b/frontend/src/components/OnboardingSteps/FolderSetupStep.tsx @@ -1,35 +1,137 @@ -import React, { useEffect, useState } from 'react'; -import { SetupScreen } from '@/components/SetupScreen'; -import { FolderService } from '@/hooks/folderService'; -import { useDispatch } from 'react-redux'; -import { markCompleted } from '@/features/onboardingSlice'; +'use client'; + +import '@/App.css'; +import { + Card, + CardHeader, + CardTitle, + CardDescription, + CardContent, + CardFooter, +} from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { FolderPlus, X, Folder } from 'lucide-react'; +import { useDispatch, useSelector } from 'react-redux'; +import { RootState, AppDispatch } from '@/store'; +import { + markCompleted, + previousStep, + addFolder, + removeFolder, +} from '@/features/onboardingSlice'; +import React from 'react'; +import { AppFeatures } from './AppFeatures'; interface FolderSetupStepProps { stepIndex: number; + totalSteps: number; } -export const FolderSetupStep: React.FC = ({ + +export function FolderSetupStep({ stepIndex, -}: FolderSetupStepProps) => { - const dispatch = useDispatch(); - const [folderPaths, setFolderPaths] = useState( - FolderService.getSavedFolderPaths(), - ); - const [showSetupScreen, setShowSetupScreen] = useState( - folderPaths.length === 0, - ); + totalSteps, +}: FolderSetupStepProps) { + const dispatch = useDispatch(); + const folders = useSelector((state: RootState) => state.onboarding.folders); - useEffect(() => { - if (folderPaths.length > 0) { - setShowSetupScreen(false); - dispatch(markCompleted(stepIndex)); - } - }, [folderPaths, stepIndex, dispatch]); + const handleSelectFolders = () => { + const mockFolder = `/home/NewFolder${folders.length + 1}`; + dispatch(addFolder(mockFolder)); + }; + + const handleRemoveFolder = (folder: string) => { + dispatch(removeFolder(folder)); + }; + + const handleNext = () => { + dispatch(markCompleted(stepIndex)); + }; - const handleFolderPathsChange = (paths: string[]) => { - setFolderPaths(paths); - FolderService.saveFolderPaths(paths); + const handleBack = () => { + dispatch(previousStep()); }; - return showSetupScreen ? ( - - ) : null; -}; + + const progressPercent = Math.round(((stepIndex + 1) / totalSteps) * 100); + + return ( +
+
+ {/* Left Card */} + + +
+ + Step {stepIndex + 1} of {totalSteps} + + {progressPercent}% +
+
+
+
+ + + Select Your Folders + + + Choose the folders you want to import + + + + + + +
+

+ Selected Folders +

+ {folders.map((folder, index) => ( +
+
+ + {folder} +
+ +
+ ))} +
+
+ + + + + + + + {/* Right Card */} + +
+
+ ); +} diff --git a/frontend/src/components/OnboardingSteps/OnboardingStep.tsx b/frontend/src/components/OnboardingSteps/OnboardingStep.tsx index cc23cf860..5cd79d345 100644 --- a/frontend/src/components/OnboardingSteps/OnboardingStep.tsx +++ b/frontend/src/components/OnboardingSteps/OnboardingStep.tsx @@ -1,25 +1,58 @@ +'use client'; + +import '@/App.css'; import React from 'react'; + import { FolderSetupStep } from '@/components/OnboardingSteps/FolderSetupStep'; -import { UpdateStep } from '@/components/OnboardingSteps/UpdateStep'; +import { AvatarSelectionStep } from '@/components/OnboardingSteps/AvatarSelectionStep'; +import { ThemeSelectionStep } from '@/components/OnboardingSteps/ThemeSelectionStep'; import { STEPS } from '@/constants/steps'; +import { UpdateStep } from '@/components/OnboardingSteps/UpdateStep'; interface OnboardingStepProps { stepIndex: number; stepName: string; + onNext: () => void; + onBack: () => void; } + +const VISIBLE_STEPS = [ + STEPS.AVATAR_SELECTION_STEP, + STEPS.FOLDER_SETUP_STEP, + STEPS.THEME_SELECTION_STEP, +]; + export const OnboardingStep: React.FC = ({ stepIndex, stepName, -}: OnboardingStepProps) => { - console.log( - `Rendering OnboardingStep: stepIndex=${stepIndex}, stepName=${stepName}`, + onNext, + onBack, +}) => { + const sharedProps = { + stepIndex, + totalSteps: VISIBLE_STEPS.length, + onNext, + onBack, + }; + + const renderStepComponent = () => { + switch (stepName) { + case STEPS.AVATAR_SELECTION_STEP: + return ; + case STEPS.FOLDER_SETUP_STEP: + return ; + case STEPS.THEME_SELECTION_STEP: + return ; + case STEPS.UPDATE_STEP: + return ; + default: + return
; + } + }; + + return ( +
+
{renderStepComponent()}
+
); - switch (stepName) { - case STEPS.UPDATE_STEP: - return ; - case STEPS.FOLDER_SETUP_STEP: - return ; - default: - return
; - } }; diff --git a/frontend/src/components/OnboardingSteps/ThemeSelectionStep.tsx b/frontend/src/components/OnboardingSteps/ThemeSelectionStep.tsx new file mode 100644 index 000000000..75db86e26 --- /dev/null +++ b/frontend/src/components/OnboardingSteps/ThemeSelectionStep.tsx @@ -0,0 +1,151 @@ +'use client'; + +import React, { useEffect, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { AppDispatch, RootState } from '@/store'; +import { + setTheme, + markCompleted, + previousStep, +} from '@/features/onboardingSlice'; + +import { Button } from '@/components/ui/button'; +import { + Card, + CardHeader, + CardTitle, + CardContent, + CardFooter, +} from '@/components/ui/card'; +import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; +import { Label } from '@/components/ui/label'; + +import { AppFeatures } from './AppFeatures'; + +interface ThemeSelectionStepProps { + stepIndex: number; + totalSteps: number; +} + +export const ThemeSelectionStep: React.FC = ({ + stepIndex, + totalSteps, +}) => { + const dispatch = useDispatch(); + const selectedTheme = useSelector( + (state: RootState) => state.onboarding.theme, + ); + + const [featureIndex, setFeatureIndex] = useState(0); + + useEffect(() => { + const stored = localStorage.getItem('theme') || 'system'; + dispatch(setTheme(stored)); + + if (stored === 'system') { + const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + document.documentElement.setAttribute( + 'data-theme', + isDark ? 'dark' : 'light', + ); + } else { + document.documentElement.setAttribute('data-theme', stored); + } + }, [dispatch]); + + useEffect(() => { + if (selectedTheme === 'system') { + const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + document.documentElement.setAttribute( + 'data-theme', + isDark ? 'dark' : 'light', + ); + } else if (selectedTheme) { + document.documentElement.setAttribute('data-theme', selectedTheme); + } + if (selectedTheme) { + localStorage.setItem('theme', selectedTheme); + } + }, [selectedTheme]); + + const handleThemeChange = (value: string) => { + dispatch(setTheme(value)); + }; + + const handleNext = () => { + dispatch(markCompleted(stepIndex)); + }; + + const handleBack = () => { + dispatch(previousStep()); + }; + + return ( +
+
+ {/* Left Card */} + + +
+ + Step {stepIndex + 1} of {totalSteps} + + {Math.round(((stepIndex + 1) / totalSteps) * 100)}% +
+
+
+
+ + + + Choose Your Theme + +

+ Select your preferred appearance +

+ + + +
+ + +
+
+ + +
+
+ + +
+
+
+ + + + + + + + {} + + +
+
+ ); +}; diff --git a/frontend/src/components/OnboardingSteps/UpdateStep.tsx b/frontend/src/components/OnboardingSteps/UpdateStep.tsx index c9cdfdda3..515b19caf 100644 --- a/frontend/src/components/OnboardingSteps/UpdateStep.tsx +++ b/frontend/src/components/OnboardingSteps/UpdateStep.tsx @@ -8,9 +8,8 @@ import { markCompleted } from '@/features/onboardingSlice'; interface UpdateStepProps { stepIndex: number; } -export const UpdateStep: React.FC = ({ - stepIndex, -}: UpdateStepProps) => { + +export const UpdateStep: React.FC = ({ stepIndex }) => { const { updateAvailable, isDownloading, @@ -20,12 +19,17 @@ export const UpdateStep: React.FC = ({ downloadAndInstall, dismissUpdate, } = useUpdater(); + const dispatch = useDispatch(); + useEffect(() => { - let check = async () => { + // Check for updates on mount + const check = async () => { dispatch(showLoader('Checking for updates...')); const hasUpdate = await checkForUpdates(); dispatch(hideLoader()); + + // If no update, mark onboarding step complete if (!hasUpdate) { dispatch(markCompleted(stepIndex)); } @@ -33,6 +37,7 @@ export const UpdateStep: React.FC = ({ check(); }, []); + // Show update dialog only if update is available if (updateAvailable) { return ( { + const [featureIndex, setFeatureIndex] = useState(0); + const currentFeature = features[featureIndex]; + + useEffect(() => { + const interval = setInterval(() => { + setFeatureIndex((prev) => (prev + 1) % features.length); + }, 3000); + + return () => clearInterval(interval); + }, []); + + return ( + + +

+ + + +
{currentFeature.icon}
+

{currentFeature.title}

+

+ {currentFeature.description} +

+ +
+ {features.map((_, index) => ( +
+ ))} +
+ + + ); +}; diff --git a/frontend/src/components/ui/label.tsx b/frontend/src/components/ui/label.tsx new file mode 100644 index 000000000..3f438eaaa --- /dev/null +++ b/frontend/src/components/ui/label.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import * as LabelPrimitive from '@radix-ui/react-label'; + +import { cn } from '@/lib/utils'; + +function Label({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { Label }; diff --git a/frontend/src/components/ui/radio-group.tsx b/frontend/src/components/ui/radio-group.tsx new file mode 100644 index 000000000..a90ec5fdf --- /dev/null +++ b/frontend/src/components/ui/radio-group.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import * as RadioGroupPrimitive from '@radix-ui/react-radio-group'; +import { CircleIcon } from 'lucide-react'; + +import { cn } from '@/lib/utils'; + +function RadioGroup({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function RadioGroupItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + + ); +} + +export { RadioGroup, RadioGroupItem }; diff --git a/frontend/src/constants/steps.ts b/frontend/src/constants/steps.ts index 078b062e5..5eea4c126 100644 --- a/frontend/src/constants/steps.ts +++ b/frontend/src/constants/steps.ts @@ -1,4 +1,6 @@ export const STEPS = { - UPDATE_STEP: 'updates', + AVATAR_SELECTION_STEP: 'avatarSelection', FOLDER_SETUP_STEP: 'folderSetup', + THEME_SELECTION_STEP: 'themeSelection', + UPDATE_STEP: 'updates', }; diff --git a/frontend/src/features/onboardingSlice.ts b/frontend/src/features/onboardingSlice.ts index 1c7493cf3..d858ac06b 100644 --- a/frontend/src/features/onboardingSlice.ts +++ b/frontend/src/features/onboardingSlice.ts @@ -7,17 +7,44 @@ interface OnboardingState { currentStepIndex: number; currentStepName: string; stepStatus: boolean[]; + avatar: string | null; + name: string; + theme: string; + folders: string[]; } + const initialState: OnboardingState = { currentStepIndex: 0, currentStepName: STEP_NAMES[0], stepStatus: STEP_NAMES.map(() => false), + avatar: null, + name: '', + theme: localStorage.getItem('theme') || 'light', + folders: ['/home/Downloads', '/home/Pictures'], }; - const onboardingSlice = createSlice({ name: 'onboarding', initialState, reducers: { + setAvatar(state, action: PayloadAction) { + state.avatar = action.payload; + }, + setName(state, action: PayloadAction) { + state.name = action.payload; + }, + setTheme(state, action: PayloadAction) { + state.theme = action.payload; + }, + addFolder(state, action: PayloadAction) { + if (!state.folders.includes(action.payload)) { + state.folders.push(action.payload); + } + }, + removeFolder(state, action: PayloadAction) { + state.folders = state.folders.filter( + (folder) => folder !== action.payload, + ); + }, markCompleted(state, action: PayloadAction) { const stepIndex = action.payload; if (stepIndex >= 0 && stepIndex < state.stepStatus.length) { @@ -27,22 +54,28 @@ const onboardingSlice = createSlice({ `Invalid step index: ${stepIndex}. Valid range: 0-${state.stepStatus.length - 1}`, ); } - // Update current step index and name state.currentStepIndex = state.stepStatus.findIndex((status) => !status); state.currentStepName = STEP_NAMES[state.currentStepIndex] || ''; }, previousStep(state) { - //Mark the last completed step as incomplete const lastCompletedIndex = state.stepStatus.lastIndexOf(true); if (lastCompletedIndex !== -1) { state.stepStatus[lastCompletedIndex] = false; } - // Update current step index and name state.currentStepIndex = state.stepStatus.findIndex((status) => !status); state.currentStepName = STEP_NAMES[state.currentStepIndex] || ''; }, }, }); -export const { markCompleted, previousStep } = onboardingSlice.actions; +export const { + setAvatar, + setName, + setTheme, + addFolder, + removeFolder, + markCompleted, + previousStep, +} = onboardingSlice.actions; + export default onboardingSlice.reducer;