diff --git a/inventory/package-lock.json b/inventory/package-lock.json index 6a2303f..546df48 100644 --- a/inventory/package-lock.json +++ b/inventory/package-lock.json @@ -36,6 +36,7 @@ "cmdk": "^1.0.0", "date-fns": "^3.6.0", "lucide-react": "^0.469.0", + "motion": "^11.18.0", "next-themes": "^0.4.4", "react": "^18.3.1", "react-chartjs-2": "^5.3.0", @@ -4606,6 +4607,33 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/framer-motion": { + "version": "11.18.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.0.tgz", + "integrity": "sha512-Vmjl5Al7XqKHzDFnVqzi1H9hzn5w4eN/bdqXTymVpU2UuMQuz9w6UPdsL9dFBeH7loBlnu4qcEXME+nvbkcIOw==", + "license": "MIT", + "dependencies": { + "motion-dom": "^11.16.4", + "motion-utils": "^11.16.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fs-extra": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", @@ -5270,6 +5298,47 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/motion": { + "version": "11.18.0", + "resolved": "https://registry.npmjs.org/motion/-/motion-11.18.0.tgz", + "integrity": "sha512-uJ4zNXh/4K9C5wftxHKlXLHC0Rc9dHSHPyO1P6T9XE2bTn2z8C2lOZX/M8vAmFp0gtJTJ3aYkv44lTtJSfv6+A==", + "license": "MIT", + "dependencies": { + "framer-motion": "^11.18.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/motion-dom": { + "version": "11.16.4", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.16.4.tgz", + "integrity": "sha512-2wuCie206pCiP2K23uvwJeci4pMFfyQKpWI0Vy6HrCTDzDCer4TsYtT7IVnuGbDeoIV37UuZiUr6SZMHEc1Vww==", + "license": "MIT", + "dependencies": { + "motion-utils": "^11.16.0" + } + }, + "node_modules/motion-utils": { + "version": "11.16.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.16.0.tgz", + "integrity": "sha512-ngdWPjg31rD4WGXFi0eZ00DQQqKKu04QExyv/ymlC+3k+WIgYVFbt6gS5JsFPbJODTF/r8XiE/X+SsoT9c0ocw==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", diff --git a/inventory/package.json b/inventory/package.json index d8eb2cd..51a702e 100644 --- a/inventory/package.json +++ b/inventory/package.json @@ -38,6 +38,7 @@ "cmdk": "^1.0.0", "date-fns": "^3.6.0", "lucide-react": "^0.469.0", + "motion": "^11.18.0", "next-themes": "^0.4.4", "react": "^18.3.1", "react-chartjs-2": "^5.3.0", diff --git a/inventory/src/App.tsx b/inventory/src/App.tsx index bae9526..6d9189b 100644 --- a/inventory/src/App.tsx +++ b/inventory/src/App.tsx @@ -2,7 +2,6 @@ import { Routes, Route, useNavigate, Navigate } from 'react-router-dom'; import { MainLayout } from './components/layout/MainLayout'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { Products } from './pages/Products'; -import { Import } from './pages/Import'; import { Dashboard } from './pages/Dashboard'; import { Orders } from './pages/Orders'; import { Settings } from './pages/Settings'; @@ -55,7 +54,6 @@ function App() { }> } /> } /> - } /> } /> } /> } /> diff --git a/inventory/src/components/layout/MainLayout.tsx b/inventory/src/components/layout/MainLayout.tsx index be1f815..71fdaa7 100644 --- a/inventory/src/components/layout/MainLayout.tsx +++ b/inventory/src/components/layout/MainLayout.tsx @@ -1,21 +1,24 @@ import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"; import { AppSidebar } from "./AppSidebar"; import { Outlet } from "react-router-dom"; +import { motion } from "motion/react"; export function MainLayout() { return ( - -
- -
-
- -
-
- -
-
-
-
+ + +
+ +
+
+ +
+
+ +
+
+
+
+
); -} \ No newline at end of file +} diff --git a/inventory/src/pages/Analytics.tsx b/inventory/src/pages/Analytics.tsx index 13baf16..bb21e04 100644 --- a/inventory/src/pages/Analytics.tsx +++ b/inventory/src/pages/Analytics.tsx @@ -7,6 +7,7 @@ import { StockAnalysis } from '../components/analytics/StockAnalysis'; import { PriceAnalysis } from '../components/analytics/PriceAnalysis'; import { CategoryPerformance } from '../components/analytics/CategoryPerformance'; import config from '../config'; +import { motion } from 'motion/react'; interface AnalyticsStats { profitMargin: number; @@ -34,7 +35,7 @@ export function Analytics() { } return ( -
+

Analytics

@@ -107,6 +108,6 @@ export function Analytics() { -
+ ); } \ No newline at end of file diff --git a/inventory/src/pages/Dashboard.tsx b/inventory/src/pages/Dashboard.tsx index d1d5ee7..c69a6d9 100644 --- a/inventory/src/pages/Dashboard.tsx +++ b/inventory/src/pages/Dashboard.tsx @@ -4,10 +4,10 @@ import { LowStockAlerts } from "@/components/dashboard/LowStockAlerts" import { TrendingProducts } from "@/components/dashboard/TrendingProducts" import { VendorPerformance } from "@/components/dashboard/VendorPerformance" import { KeyMetricsCharts } from "@/components/dashboard/KeyMetricsCharts" - +import { motion } from "motion/react" export function Dashboard() { return ( -
+

Dashboard

@@ -30,7 +30,7 @@ export function Dashboard() {
- + ) } diff --git a/inventory/src/pages/Import.tsx b/inventory/src/pages/Import.tsx deleted file mode 100644 index 9200177..0000000 --- a/inventory/src/pages/Import.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { useState } from 'react'; -import { useMutation } from '@tanstack/react-query'; -import config from '../config'; - -export function Import() { - const [file, setFile] = useState(null); - - const importMutation = useMutation({ - mutationFn: async (formData: FormData) => { - const response = await fetch(`${config.apiUrl}/products/import`, { - method: 'POST', - body: formData, - }); - if (!response.ok) { - throw new Error('Import failed'); - } - return response.json(); - }, - }); - - const handleFileChange = (e: React.ChangeEvent) => { - if (e.target.files?.[0]) { - setFile(e.target.files[0]); - } - }; - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - if (!file) return; - - const formData = new FormData(); - formData.append('file', file); - importMutation.mutate(formData); - }; - - return ( -
-

Import Products

- -
-
-
- - -

- The CSV file should contain the following columns: sku, name, description (optional), category (optional) -

-
- - - - {importMutation.isSuccess && ( -

- Successfully imported {importMutation.data.imported} products -

- )} - - {importMutation.isError && ( -

- Error importing products: {importMutation.error.message} -

- )} -
-
-
- ); -} \ No newline at end of file diff --git a/inventory/src/pages/Login.tsx b/inventory/src/pages/Login.tsx index 8cb56ff..74f18ff 100644 --- a/inventory/src/pages/Login.tsx +++ b/inventory/src/pages/Login.tsx @@ -1,22 +1,18 @@ -import { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { Button } from '@/components/ui/button'; -import { - Card, - CardContent, - CardHeader, - CardTitle, -} from '@/components/ui/card'; -import { Input } from '@/components/ui/input'; -import { toast } from 'sonner'; -import config from '../config'; -import { Loader2, Box } from 'lucide-react'; +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { toast } from "sonner"; +import config from "../config"; +import { Loader2, Box } from "lucide-react"; +import { motion } from "motion/react"; -const isDev = process.env.NODE_ENV === 'development'; +const isDev = process.env.NODE_ENV === "development"; export function Login() { - const [username, setUsername] = useState(''); - const [password, setPassword] = useState(''); + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); const [isLoading, setIsLoading] = useState(false); const navigate = useNavigate(); @@ -26,46 +22,46 @@ export function Login() { try { const url = isDev ? "/auth-inv/login" : `${config.authUrl}/login`; - console.log('Making login request:', { + console.log("Making login request:", { url, - method: 'POST', + method: "POST", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, body: { username, password }, - config + config, }); const response = await fetch(url, { - method: 'POST', + method: "POST", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, body: JSON.stringify({ username, password }), - credentials: 'include' + credentials: "include", }); - console.log('Login response status:', response.status); - + console.log("Login response status:", response.status); + if (!response.ok) { - const data = await response.json().catch(() => ({ error: 'Failed to parse error response' })); - console.error('Login failed:', data); - throw new Error(data.error || 'Login failed'); + const data = await response + .json() + .catch(() => ({ error: "Failed to parse error response" })); + console.error("Login failed:", data); + throw new Error(data.error || "Login failed"); } const data = await response.json(); - console.log('Login successful:', data); - - sessionStorage.setItem('token', data.token); - sessionStorage.setItem('isLoggedIn', 'true'); - toast.success('Successfully logged in'); - navigate('/'); + console.log("Login successful:", data); + + sessionStorage.setItem("token", data.token); + sessionStorage.setItem("isLoggedIn", "true"); + toast.success("Successfully logged in"); + navigate("/", { replace: true }); } catch (error) { - console.error('Login error:', error); + console.error("Login error:", error); toast.error( - error instanceof Error - ? error.message - : 'An unexpected error occurred', + error instanceof Error ? error.message : "An unexpected error occurred" ); } finally { setIsLoading(false); @@ -73,23 +69,33 @@ export function Login() { }; return ( -
-
-
+ +
+

Inventory Manager

-
+
- Log in to continue + + Log in to continue +
@@ -126,7 +132,7 @@ export function Login() {
-
-
+ + ); -} \ No newline at end of file +} diff --git a/inventory/src/pages/Orders.tsx b/inventory/src/pages/Orders.tsx index f48efa6..9bc13f7 100644 --- a/inventory/src/pages/Orders.tsx +++ b/inventory/src/pages/Orders.tsx @@ -33,7 +33,7 @@ import { ArrowUpDown, Search } from "lucide-react"; import debounce from 'lodash/debounce'; import config from '../config'; import { DateRange } from 'react-day-picker'; - +import { motion } from 'motion/react'; interface Order { order_number: string; customer: string; @@ -130,7 +130,7 @@ export function Orders() { ); return ( -
+

Orders

@@ -317,6 +317,6 @@ export function Orders() { )} -
+ ); } \ No newline at end of file diff --git a/inventory/src/pages/Products.tsx b/inventory/src/pages/Products.tsx index 8bf9159..61ef3fe 100644 --- a/inventory/src/pages/Products.tsx +++ b/inventory/src/pages/Products.tsx @@ -23,7 +23,7 @@ import { import { Button } from "@/components/ui/button"; import { Settings2 } from "lucide-react"; import config from '../config'; - +import { motion } from 'motion/react'; // Enhanced Product interface with all possible fields interface Product { // Basic product info (from products table) @@ -309,10 +309,11 @@ export function Products() { ); return ( - - - - + + + handlePageChange(Math.max(1, page - 1))} @@ -371,11 +372,12 @@ export function Products() { + ); }; return ( -
+

Products

@@ -451,6 +453,6 @@ export function Products() { productId={selectedProductId} onClose={() => setSelectedProductId(null)} /> -
+
); } \ No newline at end of file diff --git a/inventory/src/pages/PurchaseOrders.tsx b/inventory/src/pages/PurchaseOrders.tsx index 9c69c24..cb42db4 100644 --- a/inventory/src/pages/PurchaseOrders.tsx +++ b/inventory/src/pages/PurchaseOrders.tsx @@ -19,6 +19,7 @@ import { PaginationNext, PaginationPrevious, } from '../components/ui/pagination'; +import { motion } from 'motion/react'; interface PurchaseOrder { id: number; @@ -205,7 +206,7 @@ export default function PurchaseOrders() { } return ( -
+

Purchase Orders

{/* Metrics Overview */} @@ -453,6 +454,6 @@ export default function PurchaseOrders() { -
+ ); } \ No newline at end of file diff --git a/inventory/src/pages/Settings.tsx b/inventory/src/pages/Settings.tsx index 8e2124e..cfc7a58 100644 --- a/inventory/src/pages/Settings.tsx +++ b/inventory/src/pages/Settings.tsx @@ -3,10 +3,11 @@ import { DataManagement } from "@/components/settings/DataManagement"; import { StockManagement } from "@/components/settings/StockManagement"; import { PerformanceMetrics } from "@/components/settings/PerformanceMetrics"; import { CalculationSettings } from "@/components/settings/CalculationSettings"; +import { motion } from 'motion/react'; export function Settings() { return ( -
+

Settings

@@ -39,6 +40,6 @@ export function Settings() { -
+ ); }