diff --git a/web/src/features/demo/components/CounterDemo.tsx b/web/src/features/demo/components/CounterDemo.tsx
new file mode 100644
index 00000000..1463211b
--- /dev/null
+++ b/web/src/features/demo/components/CounterDemo.tsx
@@ -0,0 +1,70 @@
+'use client'
+
+import { Button } from '@/shared/ui/button'
+import { Box } from '@/shared/ui/box'
+import { PageHeader } from '@/shared/ui/page-header'
+import { CalculatorIcon } from '@/shared/ui/icons'
+import { useAppDispatch, useAppSelector } from '@/shared/stores/store'
+import { increment, decrement, incrementByAmount, reset } from '../stores/counterSlice'
+import { useTranslations } from 'next-intl'
+
+export function CounterDemo() {
+ const t = useTranslations('counter')
+ const count = useAppSelector((state) => state.counter.value)
+ const dispatch = useAppDispatch()
+
+ return (
+
+
}
+ iconClassName="bg-indigo-500/15 text-indigo-600 dark:bg-indigo-500/20 dark:text-indigo-400"
+ title={t('title')}
+ description={t('description')}
+ />
+
+
+
+
+
+
+ dispatch(increment())}
+ variant="default"
+ className="w-full"
+ >
+ {t('increment')}
+
+
+ dispatch(decrement())}
+ variant="outline"
+ className="w-full"
+ >
+ {t('decrement')}
+
+
+ dispatch(incrementByAmount(5))}
+ variant="secondary"
+ className="w-full"
+ >
+ {t('incrementBy5')}
+
+
+ dispatch(reset())}
+ variant="destructive"
+ className="w-full"
+ >
+ {t('reset')}
+
+
+
+
+
+ )
+}
diff --git a/web/src/features/demo/components/MultiFileUploadDemo.tsx b/web/src/features/demo/components/MultiFileUploadDemo.tsx
new file mode 100644
index 00000000..3e06612d
--- /dev/null
+++ b/web/src/features/demo/components/MultiFileUploadDemo.tsx
@@ -0,0 +1,97 @@
+'use client';
+
+import React, { useState } from 'react';
+import { useTranslations } from 'next-intl';
+import { MultiFileUpload, FileData } from '@/shared/ui/multi-file-upload';
+import { Box } from '@/shared/ui/box';
+import { Button } from '@/shared/ui/button';
+import { PageHeader } from '@/shared/ui/page-header';
+import { ImageIcon } from '@/shared/ui/icons';
+
+
+export function MultiFileUploadDemo() {
+ const t = useTranslations('multiFileUploadDemo');
+ const tMultiUpload = useTranslations('multiFileUpload');
+ const [files, setFiles] = useState([]);
+
+ const handleFilesChange = (newFiles: FileData[]) => {
+ setFiles(newFiles);
+ console.log('Files changed:', newFiles);
+ };
+
+ const handleClearAll = () => {
+ setFiles([]);
+ };
+
+
+ const getSelectedFilesText = () => {
+ const plural = files.length === 1 ? '' : 's';
+ return t('selectedFiles', { count: files.length, plural });
+ };
+
+
+ return (
+
+
}
+ iconClassName="bg-purple-500/15 text-purple-600 dark:bg-purple-500/20 dark:text-purple-400"
+ title={t('title')}
+ description={t('description')}
+ />
+
+
+
+
+ {/* Multi File Upload Example */}
+
+
+
+
+ {files.length > 0 && (
+
+
+
+ {getSelectedFilesText()}
+
+
+ {t('clearAll')}
+
+
+
+ )}
+
+ {/* Debug Section */}
+ {files.length > 0 && (
+ <>
+
+
+
{t('debugTitle')}
+
+ {JSON.stringify(
+ files.map(file => ({
+ fileName: file.file?.name,
+ fileSize: file.file?.size,
+ fileType: file.type,
+ hasPreview: !!file.preview,
+ })),
+ null,
+ 2
+ )}
+
+
+ >
+ )}
+
+
+
+
+ );
+}
diff --git a/web/src/features/demo/components/PopupDemo.tsx b/web/src/features/demo/components/PopupDemo.tsx
new file mode 100644
index 00000000..86e7c807
--- /dev/null
+++ b/web/src/features/demo/components/PopupDemo.tsx
@@ -0,0 +1,175 @@
+'use client';
+
+import { useState } from 'react';
+import { Button } from '@/shared/ui/button';
+import { Input } from '@/shared/ui/input';
+import { Box } from '@/shared/ui/box';
+import { PageHeader } from '@/shared/ui/page-header';
+import { WindowIcon } from '@/shared/ui/icons';
+import { usePopupActions } from '@/widgets/feedback-system';
+
+export default function PopupDemo() {
+ const [customInput, setCustomInput] = useState('');
+ const { alert, confirm, confirmDelete, success, error, show, close } = usePopupActions();
+
+ const handleAlert = async () => {
+ await alert({
+ title: 'Information',
+ message: 'This is a simple alert popup with an OK button.'
+ });
+ console.log('Alert closed');
+ };
+
+ const handleConfirm = async () => {
+ const result = await confirm({
+ title: 'Confirmation Required',
+ message: 'Do you want to proceed with this action?',
+ confirmText: 'Yes, Proceed',
+ cancelText: 'Cancel'
+ });
+
+ if (result) {
+ success('Action confirmed successfully!');
+ } else {
+ console.log('Action cancelled');
+ }
+ };
+
+ const handleDelete = async () => {
+ const result = await confirmDelete('This action cannot be undone. Are you sure?');
+
+ if (result) {
+ success('Item deleted successfully!');
+ }
+ };
+
+ const handleCustomPopup = () => {
+ show({
+ title: 'Custom Popup',
+ size: 'lg',
+ children: (
+
+
+ This is a custom popup with interactive content.
+
+
setCustomInput(e.target.value)}
+ />
+
+ {
+ if (customInput.trim()) {
+ success(`You entered: "${customInput}"`);
+ setCustomInput('');
+ close();
+ } else {
+ error('Please enter some text first!');
+ }
+ }}
+ >
+ Submit
+
+
+ Cancel
+
+
+
+ )
+ });
+ };
+
+ const handleTransparentPopup = () => {
+ show({
+ title: 'Transparent Background',
+ overlay: 'transparent',
+ children: (
+
+
+ This popup has a transparent background. Click outside to close.
+
+
+ Close
+
+
+ )
+ });
+ };
+
+ const handleFullScreenPopup = () => {
+ show({
+ title: 'Full Screen Content',
+ size: 'full',
+ children: (
+
+ )
+ });
+ };
+
+ return (
+
+
}
+ iconClassName="bg-orange-500/15 text-orange-600 dark:bg-orange-500/20 dark:text-orange-400"
+ title="Popup System Demo"
+ description="Interactive modal dialogs and popup components"
+ />
+
+
+
+
+
+
+ Show Alert
+
+
+
+ Show Confirmation
+
+
+
+ Delete Confirmation
+
+
+ success('This is a success message!')} variant="outline">
+ Success Message
+
+
+ error('This is an error message!')} variant="outline">
+ Error Message
+
+
+
+ Custom Popup
+
+
+
+ Transparent Background
+
+
+
+ Full Screen Popup
+
+
+
+
+
+ );
+}
diff --git a/web/src/features/demo/components/ToastDemo.tsx b/web/src/features/demo/components/ToastDemo.tsx
new file mode 100644
index 00000000..da51bfc0
--- /dev/null
+++ b/web/src/features/demo/components/ToastDemo.tsx
@@ -0,0 +1,226 @@
+'use client';
+
+import { useState } from 'react';
+import { Button } from '@/shared/ui/button';
+import { Input } from '@/shared/ui/input';
+import { Box } from '@/shared/ui/box';
+import { PageHeader } from '@/shared/ui/page-header';
+import { BellIcon } from '@/shared/ui/icons';
+import { useToast, useToastActions } from '@/shared/hooks/useToast';
+
+export default function ToastDemo() {
+ const [customMessage, setCustomMessage] = useState('');
+ const [loadingToastId, setLoadingToastId] = useState(null);
+
+ const toast = useToast();
+ const {
+ success,
+ error,
+ warning,
+ info,
+ loading,
+ promise,
+ saveSuccess,
+ deleteSuccess
+ } = useToastActions();
+
+ const handleBasicToasts = () => {
+ toast.toast('This is a basic toast message');
+ };
+
+ const handleSuccessToast = () => {
+ success('Operation completed successfully!');
+ };
+
+ const handleErrorToast = () => {
+ error('Something went wrong. Please try again.');
+ };
+
+ const handleWarningToast = () => {
+ warning('This action cannot be undone.');
+ };
+
+ const handleInfoToast = () => {
+ info('Here is some helpful information.');
+ };
+
+ const handleLoadingToast = () => {
+ const id = loading('Processing your request...');
+ setLoadingToastId(id);
+
+ // Simulate dismissing after 3 seconds
+ setTimeout(() => {
+ toast.dismiss(id);
+ success('Process completed!');
+ setLoadingToastId(null);
+ }, 3000);
+ };
+
+ const handleCustomToast = () => {
+ if (!customMessage.trim()) {
+ error('Please enter a message first!');
+ return;
+ }
+
+ toast.toast(customMessage, {
+ title: 'Custom Toast',
+ description: 'This toast has a custom message and description',
+ duration: 6000,
+ action: {
+ label: 'Undo',
+ onClick: () => {
+ info('Undo action clicked!');
+ }
+ }
+ });
+
+ setCustomMessage('');
+ };
+
+ const handlePromiseToast = async () => {
+ // Simulate an API call
+ const apiCall = new Promise((resolve, reject) => {
+ setTimeout(() => {
+ if (Math.random() > 0.5) {
+ resolve('API call successful!');
+ } else {
+ reject(new Error('API call failed'));
+ }
+ }, 2000);
+ });
+
+ try {
+ await promise(apiCall, {
+ loading: 'Making API call...',
+ success: (data) => `Success: ${data}`,
+ error: (err) => `Error: ${err instanceof Error ? err.message : String(err)}`
+ });
+ } catch {
+ // Error is handled by the promise toast
+ }
+ };
+
+ const handleMultipleToasts = () => {
+ success('First toast');
+ setTimeout(() => info('Second toast'), 500);
+ setTimeout(() => warning('Third toast'), 1000);
+ setTimeout(() => error('Fourth toast'), 1500);
+ setTimeout(() => success('Fifth toast'), 2000);
+ };
+
+ const handleConvenienceToasts = () => {
+ saveSuccess('User Profile');
+ setTimeout(() => deleteSuccess('Old File'), 1000);
+ };
+
+ const handlePositionChange = () => {
+ const positions: Array<'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'> = [
+ 'top-left', 'top-center', 'top-right',
+ 'bottom-left', 'bottom-center', 'bottom-right'
+ ];
+
+ const currentIndex = positions.indexOf(toast.position || 'bottom-right');
+ const nextPosition = positions[(currentIndex + 1) % positions.length];
+
+ toast.setPosition(nextPosition);
+ info(`Toast position changed to: ${nextPosition}`);
+ };
+
+ const handleDismissAll = () => {
+ toast.dismissAll();
+ setTimeout(() => {
+ info('All previous toasts were dismissed!');
+ }, 100);
+ };
+
+ return (
+
+
}
+ iconClassName="bg-green-500/15 text-green-600 dark:bg-green-500/20 dark:text-green-400"
+ title="Toast System Demo"
+ description="Notification system with multiple variants and positioning"
+ />
+
+
+
+
+
+
+ Basic Toast
+
+
+
+ Success Toast
+
+
+
+ Error Toast
+
+
+
+ Warning Toast
+
+
+
+ Info Toast
+
+
+
+ {loadingToastId ? 'Loading...' : 'Loading Toast'}
+
+
+
+ Promise Toast
+
+
+
+ Multiple Toasts
+
+
+
+ Convenience Toasts
+
+
+
+ Change Position
+
+
+
+ Dismiss All
+
+
+
+
+
+ setCustomMessage(e.target.value)}
+ onKeyDown={(e) => e.key === 'Enter' && handleCustomToast()}
+ />
+
+ Custom Toast
+
+
+
+
+
+ Current Position: {toast.position}
+
+
+ Max Toasts: {toast.maxToasts}
+
+
+ Default Duration: {toast.defaultDuration}ms
+
+
+ Active Toasts: {toast.toasts.length}
+
+
+
+
+
+
+ );
+}
diff --git a/web/src/features/demo/components/UserDemo.tsx b/web/src/features/demo/components/UserDemo.tsx
new file mode 100644
index 00000000..1247dc2c
--- /dev/null
+++ b/web/src/features/demo/components/UserDemo.tsx
@@ -0,0 +1,97 @@
+'use client';
+
+import { Button } from '@/shared/ui/button';
+import { Box } from '@/shared/ui/box';
+import { PageHeader } from '@/shared/ui/page-header';
+import { UserIcon, ComputerIcon, SunIcon, MoonIcon, SaveIcon } from '@/shared/ui/icons';
+import { useAppDispatch, useAppSelector } from '@/shared/stores/store';
+import { setLanguage, setTheme } from '../../../features/user/stores/userSettingsSlice';
+import { useTheme } from '@/providers/ThemeProvider';
+import { useTranslations } from 'next-intl';
+import { useRouter, usePathname } from '@/i18n/routing';
+import type { Locale } from '@/i18n/routing';
+
+export function UserDemo() {
+ const t = useTranslations('userSettings');
+ const userSettings = useAppSelector((state) => state.userSettings);
+ const dispatch = useAppDispatch();
+ const { resolvedTheme } = useTheme();
+ const router = useRouter();
+ const pathname = usePathname();
+
+ const languages: Array<{ code: Locale; name: string; flag: string }> = [
+ { code: 'en', name: 'English', flag: '🇺🇸' },
+ { code: 'ru', name: 'Русский', flag: '🇷🇺' },
+ { code: 'zh', name: '中文', flag: '🇨🇳' },
+ { code: 'es', name: 'Español', flag: '🇪🇸' },
+ { code: 'ar', name: 'العربية', flag: '🇸🇦' }
+ ];
+
+ const themes = [
+ { value: 'system', name: 'System', icon: ComputerIcon },
+ { value: 'light', name: 'Light', icon: SunIcon },
+ { value: 'dark', name: 'Dark', icon: MoonIcon }
+ ];
+
+ const getCurrentLanguage = () => languages.find(lang => lang.code === userSettings.language);
+ const getCurrentTheme = () => themes.find(theme => theme.value === userSettings.theme);
+
+ const handleLanguageChange = (newLocale: Locale) => {
+ // Update Redux store
+ dispatch(setLanguage(newLocale));
+
+ // Update URL and next-intl routing
+ router.replace(pathname, { locale: newLocale });
+ };
+
+ return (
+
+
}
+ iconClassName="bg-purple-500/15 text-purple-600 dark:bg-purple-500/20 dark:text-purple-400"
+ title={t('title')}
+ description={t('description')}
+ />
+
+
+
+ {/* Language Controls */}
+
+
Change Language:
+
+ {languages.map((lang) => (
+ handleLanguageChange(lang.code)}
+ variant={userSettings.language === lang.code ? "default" : "outline"}
+ className="w-full"
+ >
+ {lang.flag}
+ {lang.code.toUpperCase()}
+
+ ))}
+
+
+
+ {/* Theme Controls */}
+
+
Change Theme:
+
+ {themes.map((theme) => (
+ dispatch(setTheme(theme.value as 'system' | 'light' | 'dark'))}
+ variant={userSettings.theme === theme.value ? "default" : "outline"}
+ className="w-full"
+ >
+
+ {theme.name}
+
+ ))}
+
+
+
+
+
+ );
+}
diff --git a/web/src/features/demo/index.ts b/web/src/features/demo/index.ts
new file mode 100644
index 00000000..34c1d975
--- /dev/null
+++ b/web/src/features/demo/index.ts
@@ -0,0 +1,7 @@
+// Demo feature public API
+export { CounterDemo } from './components/CounterDemo';
+export { UserDemo } from './components/UserDemo';
+export { default as ToastDemo } from './components/ToastDemo';
+export { default as PopupDemo } from './components/PopupDemo';
+export { MultiFileUploadDemo } from './components/MultiFileUploadDemo';
+export { counterSlice } from './stores/counterSlice';
\ No newline at end of file