diff --git a/frontend/app/(protected)/model/page.tsx b/frontend/app/(protected)/model/page.tsx new file mode 100644 index 0000000..17a6231 --- /dev/null +++ b/frontend/app/(protected)/model/page.tsx @@ -0,0 +1,60 @@ +'use client' + +import React, { useState } from 'react' +import ModelViewer from '@/components/ModelViewer' + +export default function Home() { + const [modelInfo, setModelInfo] = useState<{ + meshes: unknown[] + boundingBox: { + min: { x: number; y: number; z: number } + max: { x: number; y: number; z: number } + } + } | null>(null) + const [error, setError] = useState(null) + + const handleModelLoaded = (data: { + meshes: unknown[] + boundingBox: { + min: { x: number; y: number; z: number } + max: { x: number; y: number; z: number } + } + }) => { + setModelInfo(data) + setError(null) + console.log('Model loaded successfully:', data) + } + + const handleError = (errorMessage: string) => { + setError(errorMessage) + setModelInfo(null) + } + + return ( +
+ + + {error && ( +
+ Error: {error} +
+ )} + + {modelInfo && ( +
+

EXPO Building Model

+ +
+
🖱️ Left click + drag: Rotate
+
🖱️ Right click + drag: Pan
+
🖱️ Scroll: Zoom in/out
+
+
+ )} +
+ ) +} diff --git a/frontend/app/default.ts b/frontend/app/default.ts new file mode 100644 index 0000000..8bc948e --- /dev/null +++ b/frontend/app/default.ts @@ -0,0 +1 @@ +export { redirect as default } from 'next/navigation' diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 00f5bb1..0a35558 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -1,60 +1,5 @@ -'use client' - -import React, { useState } from 'react' -import ModelViewer from '../components/ModelViewer' +import { redirect } from 'next/navigation' export default function Home() { - const [modelInfo, setModelInfo] = useState<{ - meshes: unknown[] - boundingBox: { - min: { x: number; y: number; z: number } - max: { x: number; y: number; z: number } - } - } | null>(null) - const [error, setError] = useState(null) - - const handleModelLoaded = (data: { - meshes: unknown[] - boundingBox: { - min: { x: number; y: number; z: number } - max: { x: number; y: number; z: number } - } - }) => { - setModelInfo(data) - setError(null) - console.log('Model loaded successfully:', data) - } - - const handleError = (errorMessage: string) => { - setError(errorMessage) - setModelInfo(null) - } - - return ( -
- - - {error && ( -
- Error: {error} -
- )} - - {modelInfo && ( -
-

EXPO Building Model

- -
-
🖱️ Left click + drag: Rotate
-
🖱️ Right click + drag: Pan
-
🖱️ Scroll: Zoom in/out
-
-
- )} -
- ) + redirect('/objects') } diff --git a/frontend/app/store/userStore.ts b/frontend/app/store/userStore.ts new file mode 100644 index 0000000..bbaec70 --- /dev/null +++ b/frontend/app/store/userStore.ts @@ -0,0 +1,47 @@ +import { create } from 'zustand' +import { persist } from 'zustand/middleware' +import { User, UserState } from '../types' + +interface UserStore extends UserState { + // состояние + isAuthenticated: boolean + user: User | null + + // действия + setUser: (user: User | null) => void + setAuthenticated: (isAuthenticated: boolean) => void + logout: () => void + + // асинхронные действия + //! что пользователь может делать асинхронно? +} + +const useUserStore = create()( + persist( + set => ({ + // начальное состояние + isAuthenticated: false, + user: null, + favorites: [], + + // синхронные действия + setUser: user => set({ user }), + setAuthenticated: isAuthenticated => set({ isAuthenticated }), + logout: () => + set({ + isAuthenticated: false, + user: null, + }), + }), + + //! асинхронщина? + { name: 'user-store' } + ) +) + +export default useUserStore + +// пример использования +// const { user, isAuthenticated } = useUserStore() -- получаем данные из стора +// const { setUser, setAuthenticated } = useUserStore() -- устанавливаем данные в стор +// const { logout } = useUserStore() -- выходим из пользовательского аккаунта diff --git a/frontend/app/types/index.ts b/frontend/app/types/index.ts index 2432b97..a5cf347 100644 --- a/frontend/app/types/index.ts +++ b/frontend/app/types/index.ts @@ -6,4 +6,29 @@ export interface ValidationRules { export interface ValidationErrors { [key: string]: string -} \ No newline at end of file +} + +export type ToastProps = { + type: 'error' | 'success' | 'loading' + message: string + action?: { + text: string + onClick: () => void + } + duration?: number +} + +export interface User { + id?: number | undefined + name: string + surname: string + image?: string + email: string + account_type?: string + login: string +} + +export interface UserState { + isAuthenticated: boolean + user: User | null +} diff --git a/frontend/components/ui/ShowToast.tsx b/frontend/components/ui/ShowToast.tsx new file mode 100644 index 0000000..7d16387 --- /dev/null +++ b/frontend/components/ui/ShowToast.tsx @@ -0,0 +1,59 @@ +import toast from 'react-hot-toast' +import { ToastProps } from '@/app/types' + +const toastStyles = { + success: { + background: 'bg-green-50', + text: 'text-green-800', + border: 'border-green-200', + }, + error: { + background: 'bg-red-50', + text: 'text-red-800', + border: 'border-red-200', + }, + loading: { + background: 'bg-blue-50', + text: 'text-blue-800', + border: 'border-blue-200', + }, +} + +const showToast = ({ type, message, action, duration }: ToastProps) => { + const styles = toastStyles[type] + + toast.custom( + t => ( +
+
+
+
+

{message}

+ {action && ( +
+ +
+ )} +
+
+
+
+ ), + { duration: duration || 4000 } + ) +} + +export default showToast + +//пример использования: showToast({ type: 'error', message: 'Неверный email или пароль' })