├── client ├── .eslintrc.json ├── src │ ├── app │ │ ├── favicon.ico │ │ ├── page.tsx │ │ ├── (components) │ │ │ ├── Header │ │ │ │ └── index.tsx │ │ │ ├── Rating │ │ │ │ └── index.tsx │ │ │ ├── Navbar │ │ │ │ └── index.tsx │ │ │ └── Sidebar │ │ │ │ └── index.tsx │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── users │ │ │ └── page.tsx │ │ ├── dashboardWrapper.tsx │ │ ├── inventory │ │ │ └── page.tsx │ │ ├── dashboard │ │ │ ├── page.tsx │ │ │ ├── CardPopularProducts.tsx │ │ │ ├── StatCard.tsx │ │ │ ├── CardPurchaseSummary.tsx │ │ │ ├── CardExpenseSummary.tsx │ │ │ └── CardSalesSummary.tsx │ │ ├── redux.tsx │ │ ├── settings │ │ │ └── page.tsx │ │ ├── products │ │ │ ├── page.tsx │ │ │ └── CreateProductModal.tsx │ │ └── expenses │ │ │ └── page.tsx │ └── state │ │ ├── index.ts │ │ └── api.ts ├── postcss.config.mjs ├── next.config.mjs ├── .gitignore ├── public │ ├── vercel.svg │ └── next.svg ├── tsconfig.json ├── package.json ├── README.md └── tailwind.config.ts ├── server ├── .gitignore ├── assets │ ├── logo.png │ ├── profile.jpg │ ├── product1.png │ ├── product2.png │ └── product3.png ├── prisma │ ├── migrations │ │ ├── migration_lock.toml │ │ └── 20240711174419_init │ │ │ └── migration.sql │ ├── seed.ts │ ├── schema.prisma │ └── seedData │ │ ├── expenses.json │ │ ├── users.json │ │ ├── purchaseSummary.json │ │ ├── products.json │ │ ├── expenseSummary.json │ │ ├── salesSummary.json │ │ ├── expenseByCategory.json │ │ ├── sales.json │ │ └── purchases.json ├── src │ ├── routes │ │ ├── userRoutes.ts │ │ ├── dashboardRoutes.ts │ │ ├── expenseRoutes.ts │ │ └── productRoutes.ts │ ├── controllers │ │ ├── userController.ts │ │ ├── expenseController.ts │ │ ├── productController.ts │ │ └── dashboardController.ts │ └── index.ts ├── ecosystem.config.js ├── package.json ├── aws-ec2-instructions.md └── tsconfig.json ├── .gitignore └── README.md /client/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | # Keep environment variables out of version control 3 | .env 4 | -------------------------------------------------------------------------------- /server/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ed-roh/inventory-management/HEAD/server/assets/logo.png -------------------------------------------------------------------------------- /server/assets/profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ed-roh/inventory-management/HEAD/server/assets/profile.jpg -------------------------------------------------------------------------------- /client/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ed-roh/inventory-management/HEAD/client/src/app/favicon.ico -------------------------------------------------------------------------------- /server/assets/product1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ed-roh/inventory-management/HEAD/server/assets/product1.png -------------------------------------------------------------------------------- /server/assets/product2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ed-roh/inventory-management/HEAD/server/assets/product2.png -------------------------------------------------------------------------------- /server/assets/product3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ed-roh/inventory-management/HEAD/server/assets/product3.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | client/node_modules/ 2 | server/node_modules/ 3 | client/dist/ 4 | server/dist/ 5 | client/.env/ 6 | server/.env/ 7 | client/.next/ -------------------------------------------------------------------------------- /client/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | import Dashboard from "@/app/dashboard/page"; 2 | 3 | export default function Home() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /server/prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /client/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /server/src/routes/userRoutes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { getUsers } from "../controllers/userController"; 3 | 4 | const router = Router(); 5 | 6 | router.get("/", getUsers); 7 | 8 | export default router; 9 | -------------------------------------------------------------------------------- /server/src/routes/dashboardRoutes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { getDashboardMetrics } from "../controllers/dashboardController"; 3 | 4 | const router = Router(); 5 | 6 | router.get("/", getDashboardMetrics); 7 | 8 | export default router; 9 | -------------------------------------------------------------------------------- /server/src/routes/expenseRoutes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { getExpensesByCategory } from "../controllers/expenseController"; 3 | 4 | const router = Router(); 5 | 6 | router.get("/", getExpensesByCategory); 7 | 8 | export default router; 9 | -------------------------------------------------------------------------------- /client/src/app/(components)/Header/index.tsx: -------------------------------------------------------------------------------- 1 | type HeaderProps = { 2 | name: string; 3 | }; 4 | 5 | const Header = ({ name }: HeaderProps) => { 6 | return

{name}

; 7 | }; 8 | 9 | export default Header; 10 | -------------------------------------------------------------------------------- /server/src/routes/productRoutes.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { createProduct, getProducts } from "../controllers/productController"; 3 | 4 | const router = Router(); 5 | 6 | router.get("/", getProducts); 7 | router.post("/", createProduct); 8 | 9 | export default router; 10 | -------------------------------------------------------------------------------- /server/ecosystem.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | apps: [ 3 | { 4 | name: "inventory-management", 5 | script: "npm", 6 | args: "run dev", 7 | env: { 8 | NODE_ENV: "development", 9 | ENV_VAR1: "environment-variable", 10 | }, 11 | }, 12 | ], 13 | }; 14 | -------------------------------------------------------------------------------- /client/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | images: { 4 | remotePatterns: [ 5 | { 6 | protocol: "https", 7 | hostname: "s3-inventorymanagement.s3.us-east-2.amazonaws.com", 8 | port: "", 9 | pathname: "/**", 10 | }, 11 | ], 12 | }, 13 | }; 14 | 15 | export default nextConfig; 16 | -------------------------------------------------------------------------------- /client/src/app/(components)/Rating/index.tsx: -------------------------------------------------------------------------------- 1 | import { Star } from "lucide-react"; 2 | import React from "react"; 3 | 4 | type RatingProps = { 5 | rating: number; 6 | }; 7 | 8 | const Rating = ({ rating }: RatingProps) => { 9 | return [1, 2, 3, 4, 5].map((index) => ( 10 | 15 | )); 16 | }; 17 | 18 | export default Rating; 19 | -------------------------------------------------------------------------------- /server/src/controllers/userController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { PrismaClient } from "@prisma/client"; 3 | 4 | const prisma = new PrismaClient(); 5 | 6 | export const getUsers = async (req: Request, res: Response): Promise => { 7 | try { 8 | const users = await prisma.users.findMany(); 9 | res.json(users); 10 | } catch (error) { 11 | res.status(500).json({ message: "Error retrieving users" }); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /client/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | *, 6 | *::before, 7 | *::after { 8 | box-sizing: border-box; 9 | } 10 | 11 | html, 12 | body, 13 | #root, 14 | .app { 15 | height: 100%; 16 | width: 100%; 17 | @apply text-sm; 18 | @apply bg-gray-500; 19 | @apply text-gray-900; 20 | } 21 | 22 | @media (min-width: 768px) { 23 | .custom-grid-rows { 24 | grid-template-rows: repeat(8, 20vh); 25 | } 26 | } 27 | 28 | @media (min-width: 1280px) { 29 | .custom-grid-rows { 30 | grid-template-rows: repeat(8, 7.5vh); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /client/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | import DashboardWrapper from "./dashboardWrapper"; 5 | 6 | const inter = Inter({ subsets: ["latin"] }); 7 | 8 | export const metadata: Metadata = { 9 | title: "Create Next App", 10 | description: "Generated by create next app", 11 | }; 12 | 13 | export default function RootLayout({ 14 | children, 15 | }: Readonly<{ 16 | children: React.ReactNode; 17 | }>) { 18 | return ( 19 | 20 | 21 | {children} 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./src/*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /client/src/state/index.ts: -------------------------------------------------------------------------------- 1 | import { createSlice, PayloadAction } from "@reduxjs/toolkit"; 2 | 3 | export interface InitialStateTypes { 4 | isSidebarCollapsed: boolean; 5 | isDarkMode: boolean; 6 | } 7 | 8 | const initialState: InitialStateTypes = { 9 | isSidebarCollapsed: false, 10 | isDarkMode: false, 11 | }; 12 | 13 | export const globalSlice = createSlice({ 14 | name: "global", 15 | initialState, 16 | reducers: { 17 | setIsSidebarCollapsed: (state, action: PayloadAction) => { 18 | state.isSidebarCollapsed = action.payload; 19 | }, 20 | setIsDarkMode: (state, action: PayloadAction) => { 21 | state.isDarkMode = action.payload; 22 | }, 23 | }, 24 | }); 25 | 26 | export const { setIsSidebarCollapsed, setIsDarkMode } = globalSlice.actions; 27 | 28 | export default globalSlice.reducer; 29 | -------------------------------------------------------------------------------- /server/src/controllers/expenseController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { PrismaClient } from "@prisma/client"; 3 | 4 | const prisma = new PrismaClient(); 5 | 6 | export const getExpensesByCategory = async ( 7 | req: Request, 8 | res: Response 9 | ): Promise => { 10 | try { 11 | const expenseByCategorySummaryRaw = await prisma.expenseByCategory.findMany( 12 | { 13 | orderBy: { 14 | date: "desc", 15 | }, 16 | } 17 | ); 18 | const expenseByCategorySummary = expenseByCategorySummaryRaw.map( 19 | (item) => ({ 20 | ...item, 21 | amount: item.amount.toString(), 22 | }) 23 | ); 24 | 25 | res.json(expenseByCategorySummary); 26 | } catch (error) { 27 | res.status(500).json({ message: "Error retrieving expenses by category" }); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "seed": "ts-node prisma/seed.ts", 9 | "build": "rimraf dist && npx tsc", 10 | "start": "npm run build && node dist/index.js", 11 | "dev": "npm run build && concurrently \"npx tsc -w\" \"nodemon --exec ts-node src/index.ts\"" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "@prisma/client": "^5.16.2", 18 | "body-parser": "^1.20.2", 19 | "concurrently": "^8.2.2", 20 | "cors": "^2.8.5", 21 | "dotenv": "^16.4.5", 22 | "express": "^4.19.2", 23 | "helmet": "^7.1.0", 24 | "morgan": "^1.10.0", 25 | "prisma": "^5.16.2", 26 | "rimraf": "^6.0.1" 27 | }, 28 | "devDependencies": { 29 | "@types/cors": "^2.8.17", 30 | "@types/express": "^4.17.21", 31 | "@types/morgan": "^1.9.9", 32 | "@types/node": "^20.14.10", 33 | "nodemon": "^3.1.4", 34 | "ts-node": "^10.9.2", 35 | "typescript": "^5.5.3" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /client/src/app/users/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useGetUsersQuery } from "@/state/api"; 4 | import Header from "@/app/(components)/Header"; 5 | import { DataGrid, GridColDef } from "@mui/x-data-grid"; 6 | 7 | const columns: GridColDef[] = [ 8 | { field: "userId", headerName: "ID", width: 90 }, 9 | { field: "name", headerName: "Name", width: 200 }, 10 | { field: "email", headerName: "Email", width: 200 }, 11 | ]; 12 | 13 | const Users = () => { 14 | const { data: users, isError, isLoading } = useGetUsersQuery(); 15 | 16 | if (isLoading) { 17 | return
Loading...
; 18 | } 19 | 20 | if (isError || !users) { 21 | return ( 22 |
Failed to fetch users
23 | ); 24 | } 25 | 26 | return ( 27 |
28 |
29 | row.userId} 33 | checkboxSelection 34 | className="bg-white shadow rounded-lg border border-gray-200 mt-5 !text-gray-700" 35 | /> 36 |
37 | ); 38 | }; 39 | 40 | export default Users; 41 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "inventory-management", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@emotion/react": "^11.11.4", 13 | "@emotion/styled": "^11.11.5", 14 | "@mui/material": "^5.16.0", 15 | "@mui/x-data-grid": "^7.9.0", 16 | "@reduxjs/toolkit": "^2.2.6", 17 | "axios": "^1.7.2", 18 | "dotenv": "^16.4.5", 19 | "lucide-react": "^0.407.0", 20 | "next": "14.2.4", 21 | "numeral": "^2.0.6", 22 | "react": "^18", 23 | "react-dom": "^18", 24 | "react-redux": "^9.1.2", 25 | "recharts": "^2.12.7", 26 | "redux-persist": "^6.0.0", 27 | "uuid": "^10.0.0" 28 | }, 29 | "devDependencies": { 30 | "@types/node": "^20.14.10", 31 | "@types/numeral": "^2.0.5", 32 | "@types/react": "^18", 33 | "@types/react-dom": "^18", 34 | "@types/uuid": "^10.0.0", 35 | "eslint": "^8", 36 | "eslint-config-next": "14.2.4", 37 | "postcss": "^8", 38 | "tailwindcss": "^3.4.1", 39 | "tw-colors": "^3.3.1", 40 | "typescript": "^5" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /server/src/controllers/productController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { PrismaClient } from "@prisma/client"; 3 | 4 | const prisma = new PrismaClient(); 5 | 6 | export const getProducts = async ( 7 | req: Request, 8 | res: Response 9 | ): Promise => { 10 | try { 11 | const search = req.query.search?.toString(); 12 | const products = await prisma.products.findMany({ 13 | where: { 14 | name: { 15 | contains: search, 16 | }, 17 | }, 18 | }); 19 | res.json(products); 20 | } catch (error) { 21 | res.status(500).json({ message: "Error retrieving products" }); 22 | } 23 | }; 24 | 25 | export const createProduct = async ( 26 | req: Request, 27 | res: Response 28 | ): Promise => { 29 | try { 30 | const { productId, name, price, rating, stockQuantity } = req.body; 31 | const product = await prisma.products.create({ 32 | data: { 33 | productId, 34 | name, 35 | price, 36 | rating, 37 | stockQuantity, 38 | }, 39 | }); 40 | res.status(201).json(product); 41 | } catch (error) { 42 | res.status(500).json({ message: "Error creating product" }); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /server/src/index.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import dotenv from "dotenv"; 3 | import bodyParser from "body-parser"; 4 | import cors from "cors"; 5 | import helmet from "helmet"; 6 | import morgan from "morgan"; 7 | /* ROUTE IMPORTS */ 8 | import dashboardRoutes from "./routes/dashboardRoutes"; 9 | import productRoutes from "./routes/productRoutes"; 10 | import userRoutes from "./routes/userRoutes"; 11 | import expenseRoutes from "./routes/expenseRoutes"; 12 | 13 | /* CONFIGURATIONS */ 14 | dotenv.config(); 15 | const app = express(); 16 | app.use(express.json()); 17 | app.use(helmet()); 18 | app.use(helmet.crossOriginResourcePolicy({ policy: "cross-origin" })); 19 | app.use(morgan("common")); 20 | app.use(bodyParser.json()); 21 | app.use(bodyParser.urlencoded({ extended: false })); 22 | app.use(cors()); 23 | 24 | /* ROUTES */ 25 | app.use("/dashboard", dashboardRoutes); // http://localhost:8000/dashboard 26 | app.use("/products", productRoutes); // http://localhost:8000/products 27 | app.use("/users", userRoutes); // http://localhost:8000/users 28 | app.use("/expenses", expenseRoutes); // http://localhost:8000/expenses 29 | 30 | /* SERVER */ 31 | const port = Number(process.env.PORT) || 3001; 32 | app.listen(port, "0.0.0.0", () => { 33 | console.log(`Server running on port ${port}`); 34 | }); 35 | -------------------------------------------------------------------------------- /client/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/app/dashboardWrapper.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React, { useEffect } from "react"; 4 | import Navbar from "@/app/(components)/Navbar"; 5 | import Sidebar from "@/app/(components)/Sidebar"; 6 | import StoreProvider, { useAppSelector } from "./redux"; 7 | 8 | const DashboardLayout = ({ children }: { children: React.ReactNode }) => { 9 | const isSidebarCollapsed = useAppSelector( 10 | (state) => state.global.isSidebarCollapsed 11 | ); 12 | const isDarkMode = useAppSelector((state) => state.global.isDarkMode); 13 | 14 | useEffect(() => { 15 | if (isDarkMode) { 16 | document.documentElement.classList.add("dark"); 17 | } else { 18 | document.documentElement.classList.add("light"); 19 | } 20 | }); 21 | 22 | return ( 23 |
28 | 29 |
34 | 35 | {children} 36 |
37 |
38 | ); 39 | }; 40 | 41 | const DashboardWrapper = ({ children }: { children: React.ReactNode }) => { 42 | return ( 43 | 44 | {children} 45 | 46 | ); 47 | }; 48 | 49 | export default DashboardWrapper; 50 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 37 | -------------------------------------------------------------------------------- /server/src/controllers/dashboardController.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { PrismaClient } from "@prisma/client"; 3 | 4 | const prisma = new PrismaClient(); 5 | 6 | export const getDashboardMetrics = async ( 7 | req: Request, 8 | res: Response 9 | ): Promise => { 10 | try { 11 | const popularProducts = await prisma.products.findMany({ 12 | take: 15, 13 | orderBy: { 14 | stockQuantity: "desc", 15 | }, 16 | }); 17 | const salesSummary = await prisma.salesSummary.findMany({ 18 | take: 5, 19 | orderBy: { 20 | date: "desc", 21 | }, 22 | }); 23 | const purchaseSummary = await prisma.purchaseSummary.findMany({ 24 | take: 5, 25 | orderBy: { 26 | date: "desc", 27 | }, 28 | }); 29 | const expenseSummary = await prisma.expenseSummary.findMany({ 30 | take: 5, 31 | orderBy: { 32 | date: "desc", 33 | }, 34 | }); 35 | const expenseByCategorySummaryRaw = await prisma.expenseByCategory.findMany( 36 | { 37 | take: 5, 38 | orderBy: { 39 | date: "desc", 40 | }, 41 | } 42 | ); 43 | const expenseByCategorySummary = expenseByCategorySummaryRaw.map( 44 | (item) => ({ 45 | ...item, 46 | amount: item.amount.toString(), 47 | }) 48 | ); 49 | 50 | res.json({ 51 | popularProducts, 52 | salesSummary, 53 | purchaseSummary, 54 | expenseSummary, 55 | expenseByCategorySummary, 56 | }); 57 | } catch (error) { 58 | res.status(500).json({ message: "Error retrieving dashboard metrics" }); 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /client/src/app/inventory/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useGetProductsQuery } from "@/state/api"; 4 | import Header from "@/app/(components)/Header"; 5 | import { DataGrid, GridColDef } from "@mui/x-data-grid"; 6 | 7 | const columns: GridColDef[] = [ 8 | { field: "productId", headerName: "ID", width: 90 }, 9 | { field: "name", headerName: "Product Name", width: 200 }, 10 | { 11 | field: "price", 12 | headerName: "Price", 13 | width: 110, 14 | type: "number", 15 | valueGetter: (value, row) => `$${row.price}`, 16 | }, 17 | { 18 | field: "rating", 19 | headerName: "Rating", 20 | width: 110, 21 | type: "number", 22 | valueGetter: (value, row) => (row.rating ? row.rating : "N/A"), 23 | }, 24 | { 25 | field: "stockQuantity", 26 | headerName: "Stock Quantity", 27 | width: 150, 28 | type: "number", 29 | }, 30 | ]; 31 | 32 | const Inventory = () => { 33 | const { data: products, isError, isLoading } = useGetProductsQuery(); 34 | 35 | if (isLoading) { 36 | return
Loading...
; 37 | } 38 | 39 | if (isError || !products) { 40 | return ( 41 |
42 | Failed to fetch products 43 |
44 | ); 45 | } 46 | 47 | return ( 48 |
49 |
50 | row.productId} 54 | checkboxSelection 55 | className="bg-white shadow rounded-lg border border-gray-200 mt-5 !text-gray-700" 56 | /> 57 |
58 | ); 59 | }; 60 | 61 | export default Inventory; 62 | -------------------------------------------------------------------------------- /client/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | import { createThemes } from "tw-colors"; 3 | import colors from "tailwindcss/colors"; 4 | 5 | const baseColors = [ 6 | "gray", 7 | "red", 8 | "yellow", 9 | "green", 10 | "blue", 11 | "indigo", 12 | "purple", 13 | "pink", 14 | ]; 15 | 16 | const shadeMapping = { 17 | "50": "900", 18 | "100": "800", 19 | "200": "700", 20 | "300": "600", 21 | "400": "500", 22 | "500": "400", 23 | "600": "300", 24 | "700": "200", 25 | "800": "100", 26 | "900": "50", 27 | }; 28 | 29 | const generateThemeObject = (colors: any, mapping: any, invert = false) => { 30 | const theme: any = {}; 31 | baseColors.forEach((color) => { 32 | theme[color] = {}; 33 | Object.entries(mapping).forEach(([key, value]: any) => { 34 | const shadeKey = invert ? value : key; 35 | theme[color][key] = colors[color][shadeKey]; 36 | }); 37 | }); 38 | return theme; 39 | }; 40 | 41 | const lightTheme = generateThemeObject(colors, shadeMapping); 42 | const darkTheme = generateThemeObject(colors, shadeMapping, true); 43 | 44 | const themes = { 45 | light: { 46 | ...lightTheme, 47 | white: "#ffffff", 48 | }, 49 | dark: { 50 | ...darkTheme, 51 | white: colors.gray["950"], 52 | black: colors.gray["50"], 53 | }, 54 | }; 55 | 56 | const config: Config = { 57 | darkMode: "class", 58 | content: [ 59 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 60 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 61 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}", 62 | ], 63 | theme: { 64 | extend: { 65 | backgroundImage: { 66 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 67 | "gradient-conic": 68 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 69 | }, 70 | }, 71 | }, 72 | plugins: [createThemes(themes)], 73 | }; 74 | 75 | export default config; 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Build a Fullstack Inventory Management Dashboard 2 | 3 | [![Tutorial Video](https://img.youtube.com/vi/ddKQ8sZo_v8/0.jpg)](https://www.youtube.com/watch?v=ddKQ8sZo_v8) 4 | 5 | Link to related video: https://www.youtube.com/watch?v=ddKQ8sZo_v8 6 | 7 | ## Tutorial 8 | 9 | This repository contains the code corresponding to an in-depth tutorial available on my YouTube channel. It is highly suggested to watch the [tutorial video](https://www.youtube.com/watch?v=ddKQ8sZo_v8) as it includes detailed instructions on how to set up everything, including deploying AWS. This tutorial is designed for both beginners and experts. 10 | 11 | Join our [Discord community](https://discord.com/channels/1070200085440376872/1267499814678171698) for discussions about this specific app. 12 | 13 | ## Tech Stack 14 | 15 | - **Next JS** 16 | - **Tailwind** 17 | - **Redux Toolkit** 18 | - **Redux Toolkit Query** 19 | - **Material UI Data Grid** 20 | - **Node.js** 21 | - **Prisma** 22 | - **AWS EC2** 23 | - **AWS RDS** 24 | - **AWS API Gateway** 25 | - **AWS Amplify** 26 | - **AWS S3** 27 | 28 | ## Resources and Links 29 | 30 | ### Image Files 31 | 32 | - [Server assets to download](https://github.com/ed-roh/inventory-management/tree/master/server/assets) 33 | 34 | ### Configuration and Code 35 | 36 | - [tailwind.config.ts](https://github.com/ed-roh/inventory-management/blob/master/client/tailwind.config.ts) (to copy) 37 | - [Redux store file](https://github.com/ed-roh/inventory-management/blob/master/client/src/app/redux.tsx) (to copy) 38 | - [Seed files for database](https://github.com/ed-roh/inventory-management/blob/master/server/prisma/seed.ts) (to copy) 39 | - [Seed data files](https://github.com/ed-roh/inventory-management/tree/master/server/prisma/seedData) (to download) 40 | 41 | ### Additional Resources 42 | 43 | - [Data model diagram](https://drawsql.app/teams/team-3023/diagrams/56-inventorymanagement) 44 | - [Prisma schema file](https://github.com/ed-roh/inventory-management/blob/master/server/prisma/schema.prisma) 45 | - [AWS commands](https://github.com/ed-roh/inventory-management/blob/master/server/aws-ec2-instructions.md) 46 | -------------------------------------------------------------------------------- /server/prisma/seed.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | import fs from "fs"; 3 | import path from "path"; 4 | const prisma = new PrismaClient(); 5 | 6 | async function deleteAllData(orderedFileNames: string[]) { 7 | const modelNames = orderedFileNames.map((fileName) => { 8 | const modelName = path.basename(fileName, path.extname(fileName)); 9 | return modelName.charAt(0).toUpperCase() + modelName.slice(1); 10 | }); 11 | 12 | for (const modelName of modelNames) { 13 | const model: any = prisma[modelName as keyof typeof prisma]; 14 | if (model) { 15 | await model.deleteMany({}); 16 | console.log(`Cleared data from ${modelName}`); 17 | } else { 18 | console.error( 19 | `Model ${modelName} not found. Please ensure the model name is correctly specified.` 20 | ); 21 | } 22 | } 23 | } 24 | 25 | async function main() { 26 | const dataDirectory = path.join(__dirname, "seedData"); 27 | 28 | const orderedFileNames = [ 29 | "products.json", 30 | "expenseSummary.json", 31 | "sales.json", 32 | "salesSummary.json", 33 | "purchases.json", 34 | "purchaseSummary.json", 35 | "users.json", 36 | "expenses.json", 37 | "expenseByCategory.json", 38 | ]; 39 | 40 | await deleteAllData(orderedFileNames); 41 | 42 | for (const fileName of orderedFileNames) { 43 | const filePath = path.join(dataDirectory, fileName); 44 | const jsonData = JSON.parse(fs.readFileSync(filePath, "utf-8")); 45 | const modelName = path.basename(fileName, path.extname(fileName)); 46 | const model: any = prisma[modelName as keyof typeof prisma]; 47 | 48 | if (!model) { 49 | console.error(`No Prisma model matches the file name: ${fileName}`); 50 | continue; 51 | } 52 | 53 | for (const data of jsonData) { 54 | await model.create({ 55 | data, 56 | }); 57 | } 58 | 59 | console.log(`Seeded ${modelName} with data from ${fileName}`); 60 | } 61 | } 62 | 63 | main() 64 | .catch((e) => { 65 | console.error(e); 66 | }) 67 | .finally(async () => { 68 | await prisma.$disconnect(); 69 | }); 70 | -------------------------------------------------------------------------------- /server/prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | // Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? 5 | // Try Prisma Accelerate: https://pris.ly/cli/accelerate-init 6 | 7 | generator client { 8 | provider = "prisma-client-js" 9 | } 10 | 11 | datasource db { 12 | provider = "postgresql" 13 | url = env("DATABASE_URL") 14 | } 15 | 16 | model Users { 17 | userId String @id 18 | name String 19 | email String 20 | } 21 | 22 | model Products { 23 | productId String @id 24 | name String 25 | price Float 26 | rating Float? 27 | stockQuantity Int 28 | Sales Sales[] 29 | Purchases Purchases[] 30 | } 31 | 32 | model Sales { 33 | saleId String @id 34 | productId String 35 | timestamp DateTime 36 | quantity Int 37 | unitPrice Float 38 | totalAmount Float 39 | product Products @relation(fields: [productId], references: [productId]) 40 | } 41 | 42 | model Purchases { 43 | purchaseId String @id 44 | productId String 45 | timestamp DateTime 46 | quantity Int 47 | unitCost Float 48 | totalCost Float 49 | product Products @relation(fields: [productId], references: [productId]) 50 | } 51 | 52 | model Expenses { 53 | expenseId String @id 54 | category String 55 | amount Float 56 | timestamp DateTime 57 | } 58 | 59 | model SalesSummary { 60 | salesSummaryId String @id 61 | totalValue Float 62 | changePercentage Float? 63 | date DateTime 64 | } 65 | 66 | model PurchaseSummary { 67 | purchaseSummaryId String @id 68 | totalPurchased Float 69 | changePercentage Float? 70 | date DateTime 71 | } 72 | 73 | model ExpenseSummary { 74 | expenseSummaryId String @id 75 | totalExpenses Float 76 | date DateTime 77 | ExpenseByCategory ExpenseByCategory[] 78 | } 79 | 80 | model ExpenseByCategory { 81 | expenseByCategoryId String @id 82 | expenseSummaryId String 83 | category String 84 | amount BigInt 85 | date DateTime 86 | expenseSummary ExpenseSummary @relation(fields: [expenseSummaryId], references: [expenseSummaryId]) 87 | } 88 | -------------------------------------------------------------------------------- /client/src/app/dashboard/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { 4 | CheckCircle, 5 | Package, 6 | Tag, 7 | TrendingDown, 8 | TrendingUp, 9 | } from "lucide-react"; 10 | import CardExpenseSummary from "./CardExpenseSummary"; 11 | import CardPopularProducts from "./CardPopularProducts"; 12 | import CardPurchaseSummary from "./CardPurchaseSummary"; 13 | import CardSalesSummary from "./CardSalesSummary"; 14 | import StatCard from "./StatCard"; 15 | 16 | const Dashboard = () => { 17 | return ( 18 |
19 | 20 | 21 | 22 | 23 | } 26 | dateRange="22 - 29 October 2023" 27 | details={[ 28 | { 29 | title: "Customer Growth", 30 | amount: "175.00", 31 | changePercentage: 131, 32 | IconComponent: TrendingUp, 33 | }, 34 | { 35 | title: "Expenses", 36 | amount: "10.00", 37 | changePercentage: -56, 38 | IconComponent: TrendingDown, 39 | }, 40 | ]} 41 | /> 42 | } 45 | dateRange="22 - 29 October 2023" 46 | details={[ 47 | { 48 | title: "Dues", 49 | amount: "250.00", 50 | changePercentage: 131, 51 | IconComponent: TrendingUp, 52 | }, 53 | { 54 | title: "Pending Orders", 55 | amount: "147", 56 | changePercentage: -56, 57 | IconComponent: TrendingDown, 58 | }, 59 | ]} 60 | /> 61 | } 64 | dateRange="22 - 29 October 2023" 65 | details={[ 66 | { 67 | title: "Sales", 68 | amount: "1000.00", 69 | changePercentage: 20, 70 | IconComponent: TrendingUp, 71 | }, 72 | { 73 | title: "Discount", 74 | amount: "200.00", 75 | changePercentage: -10, 76 | IconComponent: TrendingDown, 77 | }, 78 | ]} 79 | /> 80 |
81 | ); 82 | }; 83 | 84 | export default Dashboard; 85 | -------------------------------------------------------------------------------- /server/aws-ec2-instructions.md: -------------------------------------------------------------------------------- 1 | # EC2 Setup Instructions 2 | 3 | ## 1. Connect to EC2 Instance via EC2 Instance Connect 4 | 5 | ## 2. Install Node Version Manager (nvm) and Node.js 6 | 7 | - **Switch to superuser and install nvm:** 8 | 9 | ``` 10 | sudo su - 11 | curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash 12 | ``` 13 | 14 | - **Activate nvm:** 15 | 16 | ``` 17 | . ~/.nvm/nvm.sh 18 | ``` 19 | 20 | - **Install the latest version of Node.js using nvm:** 21 | 22 | ``` 23 | nvm install node 24 | ``` 25 | 26 | - **Verify that Node.js and npm are installed:** 27 | 28 | ``` 29 | node -v 30 | npm -v 31 | ``` 32 | 33 | ## 3. Install Git 34 | 35 | - **Update the system and install Git:** 36 | 37 | ``` 38 | sudo yum update -y 39 | sudo yum install git -y 40 | ``` 41 | 42 | - **Check Git version:** 43 | 44 | ``` 45 | git --version 46 | ``` 47 | 48 | - **Clone your code repository from GitHub:** 49 | 50 | ``` 51 | git clone [your-github-link] 52 | ``` 53 | 54 | - **Navigate to the directory and install packages:** 55 | 56 | ``` 57 | cd inventory-management 58 | npm i 59 | ``` 60 | 61 | - **Create Env File and Port 80:** 62 | 63 | ``` 64 | echo "PORT=80" > .env 65 | ``` 66 | 67 | - **Start the application:** 68 | 69 | ``` 70 | npm start 71 | ``` 72 | 73 | ## 4. Install pm2 (Production Process Manager for Node.js) 74 | 75 | - **Install pm2 globally:** 76 | 77 | ``` 78 | npm i pm2 -g 79 | ``` 80 | 81 | - **Create a pm2 ecosystem configuration file (inside server directory):** 82 | 83 | ``` 84 | module.exports = { apps : [{ name: 'inventory-management', script: 'npm', args: 'run dev', env: { NODE_ENV: 'development', ENV_VAR1: 'environment-variable', } }], }; 85 | ``` 86 | 87 | - **Modify the ecosystem file if necessary:** 88 | 89 | ``` 90 | nano ecosystem.config.js 91 | ``` 92 | 93 | - **Set pm2 to restart automatically on system reboot:** 94 | 95 | ``` 96 | sudo env PATH=$PATH:$(which node) $(which pm2) startup systemd -u $USER --hp $(eval echo ~$USER) 97 | ``` 98 | 99 | - **Start the application using the pm2 ecosystem configuration:** 100 | 101 | ``` 102 | pm2 start ecosystem.config.js 103 | ``` 104 | 105 | - **Useful pm2 commands:** 106 | 107 | - **Stop all processes:** 108 | 109 | ``` 110 | pm2 stop all 111 | ``` 112 | 113 | - **Delete all processes:** 114 | 115 | ``` 116 | pm2 delete all 117 | ``` 118 | 119 | - **Check status of processes:** 120 | 121 | ``` 122 | pm2 status 123 | ``` 124 | 125 | - **Monitor processes:** 126 | 127 | ``` 128 | pm2 monit 129 | ``` 130 | -------------------------------------------------------------------------------- /client/src/app/dashboard/CardPopularProducts.tsx: -------------------------------------------------------------------------------- 1 | import { useGetDashboardMetricsQuery } from "@/state/api"; 2 | import { ShoppingBag } from "lucide-react"; 3 | import React from "react"; 4 | import Rating from "../(components)/Rating"; 5 | import Image from "next/image"; 6 | 7 | const CardPopularProducts = () => { 8 | const { data: dashboardMetrics, isLoading } = useGetDashboardMetricsQuery(); 9 | 10 | return ( 11 |
12 | {isLoading ? ( 13 |
Loading...
14 | ) : ( 15 | <> 16 |

17 | Popular Products 18 |

19 |
20 |
21 | {dashboardMetrics?.popularProducts.map((product) => ( 22 |
26 |
27 | {product.name} 36 |
37 |
38 | {product.name} 39 |
40 |
41 | 42 | ${product.price} 43 | 44 | | 45 | 46 |
47 |
48 |
49 | 50 |
51 | 54 | {Math.round(product.stockQuantity / 1000)}k Sold 55 |
56 |
57 | ))} 58 |
59 | 60 | )} 61 |
62 | ); 63 | }; 64 | 65 | export default CardPopularProducts; 66 | -------------------------------------------------------------------------------- /client/src/app/dashboard/StatCard.tsx: -------------------------------------------------------------------------------- 1 | import { LucideIcon } from "lucide-react"; 2 | import React from "react"; 3 | 4 | type StatDetail = { 5 | title: string; 6 | amount: string; 7 | changePercentage: number; 8 | IconComponent: LucideIcon; 9 | }; 10 | 11 | type StatCardProps = { 12 | title: string; 13 | primaryIcon: JSX.Element; 14 | details: StatDetail[]; 15 | dateRange: string; 16 | }; 17 | 18 | const StatCard = ({ 19 | title, 20 | primaryIcon, 21 | details, 22 | dateRange, 23 | }: StatCardProps) => { 24 | const formatPercentage = (value: number) => { 25 | const signal = value >= 0 ? "+" : ""; 26 | return `${signal}${value.toFixed()}%`; 27 | }; 28 | 29 | const getChangeColor = (value: number) => 30 | value >= 0 ? "text-green-500" : "text-red-500"; 31 | 32 | return ( 33 |
34 | {/* HEADER */} 35 |
36 |
37 |

{title}

38 | {dateRange} 39 |
40 |
41 |
42 | 43 | {/* BODY */} 44 |
45 |
46 | {primaryIcon} 47 |
48 |
49 | {details.map((detail, index) => ( 50 | 51 |
52 | {detail.title} 53 | {detail.amount} 54 |
55 | 60 | 61 | 66 | {formatPercentage(detail.changePercentage)} 67 | 68 |
69 |
70 | {index < details.length - 1 &&
} 71 |
72 | ))} 73 |
74 |
75 |
76 | ); 77 | }; 78 | 79 | export default StatCard; 80 | -------------------------------------------------------------------------------- /server/prisma/seedData/expenses.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "expenseId": "5c1121d7-20c8-4890-81c0-949bd60523a3", 4 | "category": "Salaries", 5 | "amount": 1489153.5, 6 | "timestamp": "2022-08-27T18:35:57Z" 7 | }, 8 | { 9 | "expenseId": "4529aaef-39d0-4188-9256-8c8fd5bad7ca", 10 | "category": "Office", 11 | "amount": 1579039.97, 12 | "timestamp": "2020-05-12T19:39:50Z" 13 | }, 14 | { 15 | "expenseId": "fca4f2d0-c3d7-4246-a04e-626ab4045c40", 16 | "category": "Office", 17 | "amount": 607415.3, 18 | "timestamp": "2021-06-08T13:48:10Z" 19 | }, 20 | { 21 | "expenseId": "34de9517-153e-4505-9af8-dc8440e28dd9", 22 | "category": "Salaries", 23 | "amount": 171044.25, 24 | "timestamp": "2022-10-03T22:21:20Z" 25 | }, 26 | { 27 | "expenseId": "c21c08e8-51b9-40f1-be37-b5116cd8d174", 28 | "category": "Salaries", 29 | "amount": 303743.89, 30 | "timestamp": "2020-03-18T00:41:00Z" 31 | }, 32 | { 33 | "expenseId": "fb2dba2d-c186-4165-ab7d-66ce276041a4", 34 | "category": "Salaries", 35 | "amount": 1707710.31, 36 | "timestamp": "2020-05-04T20:18:41Z" 37 | }, 38 | { 39 | "expenseId": "ba5ded78-639c-46ce-80bf-7d50642a1438", 40 | "category": "Office", 41 | "amount": 1375535.38, 42 | "timestamp": "2021-11-04T02:56:56Z" 43 | }, 44 | { 45 | "expenseId": "c2f642b5-399b-48d7-9deb-e6a4eed0b9fa", 46 | "category": "Office", 47 | "amount": 858502.63, 48 | "timestamp": "2020-09-04T19:49:29Z" 49 | }, 50 | { 51 | "expenseId": "fc8b38d8-388a-4898-9ca5-b942bbbec035", 52 | "category": "Professional", 53 | "amount": 1557261.18, 54 | "timestamp": "2020-07-22T02:35:40Z" 55 | }, 56 | { 57 | "expenseId": "36764f05-d204-4f71-bbd3-9d0263ea6863", 58 | "category": "Salaries", 59 | "amount": 586616.74, 60 | "timestamp": "2021-01-18T01:06:07Z" 61 | }, 62 | { 63 | "expenseId": "39e86828-36f4-4282-9599-bc4c42bae3a1", 64 | "category": "Office", 65 | "amount": 1677605.54, 66 | "timestamp": "2022-07-12T16:33:04Z" 67 | }, 68 | { 69 | "expenseId": "66a3d687-05b0-4796-9b09-14941aa73031", 70 | "category": "Salaries", 71 | "amount": 1280249.59, 72 | "timestamp": "2021-09-09T05:45:26Z" 73 | }, 74 | { 75 | "expenseId": "cfbd98f2-c6ed-4bcc-b604-f3d330c5f31e", 76 | "category": "Professional", 77 | "amount": 1851906.49, 78 | "timestamp": "2021-12-24T09:18:27Z" 79 | }, 80 | { 81 | "expenseId": "c68ccd7e-601c-4b09-b76f-c5a19308e526", 82 | "category": "Salaries", 83 | "amount": 1791685.13, 84 | "timestamp": "2022-08-05T03:18:23Z" 85 | }, 86 | { 87 | "expenseId": "3f19431c-3403-468b-9421-c9f54d466052", 88 | "category": "Salaries", 89 | "amount": 589917.71, 90 | "timestamp": "2021-05-19T17:43:00Z" 91 | } 92 | ] 93 | -------------------------------------------------------------------------------- /client/src/app/redux.tsx: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import { combineReducers, configureStore } from "@reduxjs/toolkit"; 3 | import { 4 | TypedUseSelectorHook, 5 | useDispatch, 6 | useSelector, 7 | Provider, 8 | } from "react-redux"; 9 | import globalReducer from "@/state"; 10 | import { api } from "@/state/api"; 11 | import { setupListeners } from "@reduxjs/toolkit/query"; 12 | 13 | import { 14 | persistStore, 15 | persistReducer, 16 | FLUSH, 17 | REHYDRATE, 18 | PAUSE, 19 | PERSIST, 20 | PURGE, 21 | REGISTER, 22 | } from "redux-persist"; 23 | import { PersistGate } from "redux-persist/integration/react"; 24 | import createWebStorage from "redux-persist/lib/storage/createWebStorage"; 25 | 26 | /* REDUX PERSISTENCE */ 27 | const createNoopStorage = () => { 28 | return { 29 | getItem(_key: any) { 30 | return Promise.resolve(null); 31 | }, 32 | setItem(_key: any, value: any) { 33 | return Promise.resolve(value); 34 | }, 35 | removeItem(_key: any) { 36 | return Promise.resolve(); 37 | }, 38 | }; 39 | }; 40 | 41 | const storage = 42 | typeof window === "undefined" 43 | ? createNoopStorage() 44 | : createWebStorage("local"); 45 | 46 | const persistConfig = { 47 | key: "root", 48 | storage, 49 | whitelist: ["global"], 50 | }; 51 | const rootReducer = combineReducers({ 52 | global: globalReducer, 53 | [api.reducerPath]: api.reducer, 54 | }); 55 | const persistedReducer = persistReducer(persistConfig, rootReducer); 56 | 57 | /* REDUX STORE */ 58 | export const makeStore = () => { 59 | return configureStore({ 60 | reducer: persistedReducer, 61 | middleware: (getDefaultMiddleware) => 62 | getDefaultMiddleware({ 63 | serializableCheck: { 64 | ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], 65 | }, 66 | }).concat(api.middleware), 67 | }); 68 | }; 69 | 70 | /* REDUX TYPES */ 71 | export type AppStore = ReturnType; 72 | export type RootState = ReturnType; 73 | export type AppDispatch = AppStore["dispatch"]; 74 | export const useAppDispatch = () => useDispatch(); 75 | export const useAppSelector: TypedUseSelectorHook = useSelector; 76 | 77 | /* PROVIDER */ 78 | export default function StoreProvider({ 79 | children, 80 | }: { 81 | children: React.ReactNode; 82 | }) { 83 | const storeRef = useRef(); 84 | if (!storeRef.current) { 85 | storeRef.current = makeStore(); 86 | setupListeners(storeRef.current.dispatch); 87 | } 88 | const persistor = persistStore(storeRef.current); 89 | 90 | return ( 91 | 92 | 93 | {children} 94 | 95 | 96 | ); 97 | } 98 | -------------------------------------------------------------------------------- /client/src/state/api.ts: -------------------------------------------------------------------------------- 1 | import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react"; 2 | 3 | export interface Product { 4 | productId: string; 5 | name: string; 6 | price: number; 7 | rating?: number; 8 | stockQuantity: number; 9 | } 10 | 11 | export interface NewProduct { 12 | name: string; 13 | price: number; 14 | rating?: number; 15 | stockQuantity: number; 16 | } 17 | 18 | export interface SalesSummary { 19 | salesSummaryId: string; 20 | totalValue: number; 21 | changePercentage?: number; 22 | date: string; 23 | } 24 | 25 | export interface PurchaseSummary { 26 | purchaseSummaryId: string; 27 | totalPurchased: number; 28 | changePercentage?: number; 29 | date: string; 30 | } 31 | 32 | export interface ExpenseSummary { 33 | expenseSummarId: string; 34 | totalExpenses: number; 35 | date: string; 36 | } 37 | 38 | export interface ExpenseByCategorySummary { 39 | expenseByCategorySummaryId: string; 40 | category: string; 41 | amount: string; 42 | date: string; 43 | } 44 | 45 | export interface DashboardMetrics { 46 | popularProducts: Product[]; 47 | salesSummary: SalesSummary[]; 48 | purchaseSummary: PurchaseSummary[]; 49 | expenseSummary: ExpenseSummary[]; 50 | expenseByCategorySummary: ExpenseByCategorySummary[]; 51 | } 52 | 53 | export interface User { 54 | userId: string; 55 | name: string; 56 | email: string; 57 | } 58 | 59 | export const api = createApi({ 60 | baseQuery: fetchBaseQuery({ baseUrl: process.env.NEXT_PUBLIC_API_BASE_URL }), 61 | reducerPath: "api", 62 | tagTypes: ["DashboardMetrics", "Products", "Users", "Expenses"], 63 | endpoints: (build) => ({ 64 | getDashboardMetrics: build.query({ 65 | query: () => "/dashboard", 66 | providesTags: ["DashboardMetrics"], 67 | }), 68 | getProducts: build.query({ 69 | query: (search) => ({ 70 | url: "/products", 71 | params: search ? { search } : {}, 72 | }), 73 | providesTags: ["Products"], 74 | }), 75 | createProduct: build.mutation({ 76 | query: (newProduct) => ({ 77 | url: "/products", 78 | method: "POST", 79 | body: newProduct, 80 | }), 81 | invalidatesTags: ["Products"], 82 | }), 83 | getUsers: build.query({ 84 | query: () => "/users", 85 | providesTags: ["Users"], 86 | }), 87 | getExpensesByCategory: build.query({ 88 | query: () => "/expenses", 89 | providesTags: ["Expenses"], 90 | }), 91 | }), 92 | }); 93 | 94 | export const { 95 | useGetDashboardMetricsQuery, 96 | useGetProductsQuery, 97 | useCreateProductMutation, 98 | useGetUsersQuery, 99 | useGetExpensesByCategoryQuery, 100 | } = api; 101 | -------------------------------------------------------------------------------- /client/src/app/(components)/Navbar/index.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useAppDispatch, useAppSelector } from "@/app/redux"; 4 | import { setIsDarkMode, setIsSidebarCollapsed } from "@/state"; 5 | import { Bell, Menu, Moon, Settings, Sun } from "lucide-react"; 6 | import Image from "next/image"; 7 | import Link from "next/link"; 8 | import React from "react"; 9 | 10 | const Navbar = () => { 11 | const dispatch = useAppDispatch(); 12 | const isSidebarCollapsed = useAppSelector( 13 | (state) => state.global.isSidebarCollapsed 14 | ); 15 | const isDarkMode = useAppSelector((state) => state.global.isDarkMode); 16 | 17 | const toggleSidebar = () => { 18 | dispatch(setIsSidebarCollapsed(!isSidebarCollapsed)); 19 | }; 20 | 21 | const toggleDarkMode = () => { 22 | dispatch(setIsDarkMode(!isDarkMode)); 23 | }; 24 | 25 | return ( 26 |
27 | {/* LEFT SIDE */} 28 |
29 | 35 | 36 |
37 | 42 | 43 |
44 | 45 |
46 |
47 |
48 | 49 | {/* RIGHT SIDE */} 50 |
51 |
52 |
53 | 60 |
61 |
62 | 63 | 64 | 3 65 | 66 |
67 |
68 |
69 | Profile 76 | Ed Roh 77 |
78 |
79 | 80 | 81 | 82 |
83 |
84 | ); 85 | }; 86 | 87 | export default Navbar; 88 | -------------------------------------------------------------------------------- /server/prisma/migrations/20240711174419_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Users" ( 3 | "userId" TEXT NOT NULL, 4 | "name" TEXT NOT NULL, 5 | "email" TEXT NOT NULL, 6 | 7 | CONSTRAINT "Users_pkey" PRIMARY KEY ("userId") 8 | ); 9 | 10 | -- CreateTable 11 | CREATE TABLE "Products" ( 12 | "productId" TEXT NOT NULL, 13 | "name" TEXT NOT NULL, 14 | "price" DOUBLE PRECISION NOT NULL, 15 | "rating" DOUBLE PRECISION, 16 | "stockQuantity" INTEGER NOT NULL, 17 | 18 | CONSTRAINT "Products_pkey" PRIMARY KEY ("productId") 19 | ); 20 | 21 | -- CreateTable 22 | CREATE TABLE "Sales" ( 23 | "saleId" TEXT NOT NULL, 24 | "productId" TEXT NOT NULL, 25 | "timestamp" TIMESTAMP(3) NOT NULL, 26 | "quantity" INTEGER NOT NULL, 27 | "unitPrice" DOUBLE PRECISION NOT NULL, 28 | "totalAmount" DOUBLE PRECISION NOT NULL, 29 | 30 | CONSTRAINT "Sales_pkey" PRIMARY KEY ("saleId") 31 | ); 32 | 33 | -- CreateTable 34 | CREATE TABLE "Purchases" ( 35 | "purchaseId" TEXT NOT NULL, 36 | "productId" TEXT NOT NULL, 37 | "timestamp" TIMESTAMP(3) NOT NULL, 38 | "quantity" INTEGER NOT NULL, 39 | "unitCost" DOUBLE PRECISION NOT NULL, 40 | "totalCost" DOUBLE PRECISION NOT NULL, 41 | 42 | CONSTRAINT "Purchases_pkey" PRIMARY KEY ("purchaseId") 43 | ); 44 | 45 | -- CreateTable 46 | CREATE TABLE "Expenses" ( 47 | "expenseId" TEXT NOT NULL, 48 | "category" TEXT NOT NULL, 49 | "amount" DOUBLE PRECISION NOT NULL, 50 | "timestamp" TIMESTAMP(3) NOT NULL, 51 | 52 | CONSTRAINT "Expenses_pkey" PRIMARY KEY ("expenseId") 53 | ); 54 | 55 | -- CreateTable 56 | CREATE TABLE "SalesSummary" ( 57 | "salesSummaryId" TEXT NOT NULL, 58 | "totalValue" DOUBLE PRECISION NOT NULL, 59 | "changePercentage" DOUBLE PRECISION, 60 | "date" TIMESTAMP(3) NOT NULL, 61 | 62 | CONSTRAINT "SalesSummary_pkey" PRIMARY KEY ("salesSummaryId") 63 | ); 64 | 65 | -- CreateTable 66 | CREATE TABLE "PurchaseSummary" ( 67 | "purchaseSummaryId" TEXT NOT NULL, 68 | "totalPurchased" DOUBLE PRECISION NOT NULL, 69 | "changePercentage" DOUBLE PRECISION, 70 | "date" TIMESTAMP(3) NOT NULL, 71 | 72 | CONSTRAINT "PurchaseSummary_pkey" PRIMARY KEY ("purchaseSummaryId") 73 | ); 74 | 75 | -- CreateTable 76 | CREATE TABLE "ExpenseSummary" ( 77 | "expenseSummaryId" TEXT NOT NULL, 78 | "totalExpenses" DOUBLE PRECISION NOT NULL, 79 | "date" TIMESTAMP(3) NOT NULL, 80 | 81 | CONSTRAINT "ExpenseSummary_pkey" PRIMARY KEY ("expenseSummaryId") 82 | ); 83 | 84 | -- CreateTable 85 | CREATE TABLE "ExpenseByCategory" ( 86 | "expenseByCategoryId" TEXT NOT NULL, 87 | "expenseSummaryId" TEXT NOT NULL, 88 | "category" TEXT NOT NULL, 89 | "amount" BIGINT NOT NULL, 90 | "date" TIMESTAMP(3) NOT NULL, 91 | 92 | CONSTRAINT "ExpenseByCategory_pkey" PRIMARY KEY ("expenseByCategoryId") 93 | ); 94 | 95 | -- AddForeignKey 96 | ALTER TABLE "Sales" ADD CONSTRAINT "Sales_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Products"("productId") ON DELETE RESTRICT ON UPDATE CASCADE; 97 | 98 | -- AddForeignKey 99 | ALTER TABLE "Purchases" ADD CONSTRAINT "Purchases_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Products"("productId") ON DELETE RESTRICT ON UPDATE CASCADE; 100 | 101 | -- AddForeignKey 102 | ALTER TABLE "ExpenseByCategory" ADD CONSTRAINT "ExpenseByCategory_expenseSummaryId_fkey" FOREIGN KEY ("expenseSummaryId") REFERENCES "ExpenseSummary"("expenseSummaryId") ON DELETE RESTRICT ON UPDATE CASCADE; 103 | -------------------------------------------------------------------------------- /client/src/app/settings/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React, { useState } from "react"; 4 | import Header from "@/app/(components)/Header"; 5 | 6 | type UserSetting = { 7 | label: string; 8 | value: string | boolean; 9 | type: "text" | "toggle"; 10 | }; 11 | 12 | const mockSettings: UserSetting[] = [ 13 | { label: "Username", value: "john_doe", type: "text" }, 14 | { label: "Email", value: "john.doe@example.com", type: "text" }, 15 | { label: "Notification", value: true, type: "toggle" }, 16 | { label: "Dark Mode", value: false, type: "toggle" }, 17 | { label: "Language", value: "English", type: "text" }, 18 | ]; 19 | 20 | const Settings = () => { 21 | const [userSettings, setUserSettings] = useState(mockSettings); 22 | 23 | const handleToggleChange = (index: number) => { 24 | const settingsCopy = [...userSettings]; 25 | settingsCopy[index].value = !settingsCopy[index].value as boolean; 26 | setUserSettings(settingsCopy); 27 | }; 28 | 29 | return ( 30 |
31 |
32 |
33 | 34 | 35 | 36 | 39 | 42 | 43 | 44 | 45 | {userSettings.map((setting, index) => ( 46 | 47 | 48 | 78 | 79 | ))} 80 | 81 |
37 | Setting 38 | 40 | Value 41 |
{setting.label} 49 | {setting.type === "toggle" ? ( 50 | 65 | ) : ( 66 | { 71 | const settingsCopy = [...userSettings]; 72 | settingsCopy[index].value = e.target.value; 73 | setUserSettings(settingsCopy); 74 | }} 75 | /> 76 | )} 77 |
82 |
83 |
84 | ); 85 | }; 86 | 87 | export default Settings; 88 | -------------------------------------------------------------------------------- /client/src/app/dashboard/CardPurchaseSummary.tsx: -------------------------------------------------------------------------------- 1 | import { useGetDashboardMetricsQuery } from "@/state/api"; 2 | import { TrendingDown, TrendingUp } from "lucide-react"; 3 | import numeral from "numeral"; 4 | import React from "react"; 5 | import { 6 | Area, 7 | AreaChart, 8 | ResponsiveContainer, 9 | Tooltip, 10 | XAxis, 11 | YAxis, 12 | } from "recharts"; 13 | 14 | const CardPurchaseSummary = () => { 15 | const { data, isLoading } = useGetDashboardMetricsQuery(); 16 | const purchaseData = data?.purchaseSummary || []; 17 | 18 | const lastDataPoint = purchaseData[purchaseData.length - 1] || null; 19 | 20 | return ( 21 |
22 | {isLoading ? ( 23 |
Loading...
24 | ) : ( 25 | <> 26 | {/* HEADER */} 27 |
28 |

29 | Purchase Summary 30 |

31 |
32 |
33 | 34 | {/* BODY */} 35 |
36 | {/* BODY HEADER */} 37 |
38 |

Purchased

39 |
40 |

41 | {lastDataPoint 42 | ? numeral(lastDataPoint.totalPurchased).format("$0.00a") 43 | : "0"} 44 |

45 | {lastDataPoint && ( 46 |

= 0 49 | ? "text-green-500" 50 | : "text-red-500" 51 | } flex ml-3`} 52 | > 53 | {lastDataPoint.changePercentage! >= 0 ? ( 54 | 55 | ) : ( 56 | 57 | )} 58 | {Math.abs(lastDataPoint.changePercentage!)}% 59 |

60 | )} 61 |
62 |
63 | {/* CHART */} 64 | 65 | 69 | 70 | 71 | [ 73 | `$${value.toLocaleString("en")}`, 74 | ]} 75 | labelFormatter={(label) => { 76 | const date = new Date(label); 77 | return date.toLocaleDateString("en-US", { 78 | year: "numeric", 79 | month: "long", 80 | day: "numeric", 81 | }); 82 | }} 83 | /> 84 | 91 | 92 | 93 |
94 | 95 | )} 96 |
97 | ); 98 | }; 99 | 100 | export default CardPurchaseSummary; 101 | -------------------------------------------------------------------------------- /client/src/app/products/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useCreateProductMutation, useGetProductsQuery } from "@/state/api"; 4 | import { PlusCircleIcon, SearchIcon } from "lucide-react"; 5 | import { useState } from "react"; 6 | import Header from "@/app/(components)/Header"; 7 | import Rating from "@/app/(components)/Rating"; 8 | import CreateProductModal from "./CreateProductModal"; 9 | import Image from "next/image"; 10 | 11 | type ProductFormData = { 12 | name: string; 13 | price: number; 14 | stockQuantity: number; 15 | rating: number; 16 | }; 17 | 18 | const Products = () => { 19 | const [searchTerm, setSearchTerm] = useState(""); 20 | const [isModalOpen, setIsModalOpen] = useState(false); 21 | 22 | const { 23 | data: products, 24 | isLoading, 25 | isError, 26 | } = useGetProductsQuery(searchTerm); 27 | 28 | const [createProduct] = useCreateProductMutation(); 29 | const handleCreateProduct = async (productData: ProductFormData) => { 30 | await createProduct(productData); 31 | }; 32 | 33 | if (isLoading) { 34 | return
Loading...
; 35 | } 36 | 37 | if (isError || !products) { 38 | return ( 39 |
40 | Failed to fetch products 41 |
42 | ); 43 | } 44 | 45 | return ( 46 |
47 | {/* SEARCH BAR */} 48 |
49 |
50 | 51 | setSearchTerm(e.target.value)} 56 | /> 57 |
58 |
59 | 60 | {/* HEADER BAR */} 61 |
62 |
63 | 70 |
71 | 72 | {/* BODY PRODUCTS LIST */} 73 |
74 | {isLoading ? ( 75 |
Loading...
76 | ) : ( 77 | products?.map((product) => ( 78 |
82 |
83 | {product.name} 92 |

93 | {product.name} 94 |

95 |

${product.price.toFixed(2)}

96 |
97 | Stock: {product.stockQuantity} 98 |
99 | {product.rating && ( 100 |
101 | 102 |
103 | )} 104 |
105 |
106 | )) 107 | )} 108 |
109 | 110 | {/* MODAL */} 111 | setIsModalOpen(false)} 114 | onCreate={handleCreateProduct} 115 | /> 116 |
117 | ); 118 | }; 119 | 120 | export default Products; 121 | -------------------------------------------------------------------------------- /client/src/app/products/CreateProductModal.tsx: -------------------------------------------------------------------------------- 1 | import React, { ChangeEvent, FormEvent, useState } from "react"; 2 | import { v4 } from "uuid"; 3 | import Header from "@/app/(components)/Header"; 4 | 5 | type ProductFormData = { 6 | name: string; 7 | price: number; 8 | stockQuantity: number; 9 | rating: number; 10 | }; 11 | 12 | type CreateProductModalProps = { 13 | isOpen: boolean; 14 | onClose: () => void; 15 | onCreate: (formData: ProductFormData) => void; 16 | }; 17 | 18 | const CreateProductModal = ({ 19 | isOpen, 20 | onClose, 21 | onCreate, 22 | }: CreateProductModalProps) => { 23 | const [formData, setFormData] = useState({ 24 | productId: v4(), 25 | name: "", 26 | price: 0, 27 | stockQuantity: 0, 28 | rating: 0, 29 | }); 30 | 31 | const handleChange = (e: ChangeEvent) => { 32 | const { name, value } = e.target; 33 | setFormData({ 34 | ...formData, 35 | [name]: 36 | name === "price" || name === "stockQuantity" || name === "rating" 37 | ? parseFloat(value) 38 | : value, 39 | }); 40 | }; 41 | 42 | const handleSubmit = (e: FormEvent) => { 43 | e.preventDefault(); 44 | onCreate(formData); 45 | onClose(); 46 | }; 47 | 48 | if (!isOpen) return null; 49 | 50 | const labelCssStyles = "block text-sm font-medium text-gray-700"; 51 | const inputCssStyles = 52 | "block w-full mb-2 p-2 border-gray-500 border-2 rounded-md"; 53 | 54 | return ( 55 |
56 |
57 |
58 |
59 | {/* PRODUCT NAME */} 60 | 63 | 72 | 73 | {/* PRICE */} 74 | 77 | 86 | 87 | {/* STOCK QUANTITY */} 88 | 91 | 100 | 101 | {/* RATING */} 102 | 105 | 114 | 115 | {/* CREATE ACTIONS */} 116 | 122 | 129 |
130 |
131 |
132 | ); 133 | }; 134 | 135 | export default CreateProductModal; 136 | -------------------------------------------------------------------------------- /server/prisma/seedData/users.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "userId": "3b0fd66b-a4d6-4d95-94e4-01940c99aedb", 4 | "name": "Carly", 5 | "email": "cvansalzberger0@cisco.com" 6 | }, 7 | { 8 | "userId": "d9d323fa-5c98-4222-a352-120e1f5e2798", 9 | "name": "Inesita", 10 | "email": "imcconnachie1@oaic.gov.au" 11 | }, 12 | { 13 | "userId": "9e2895ae-4afe-4ff2-b3b3-be15cf1c82d6", 14 | "name": "Tulley", 15 | "email": "tbridywater2@wikimedia.org" 16 | }, 17 | { 18 | "userId": "c7072fb5-cd2b-4703-8a58-328e5b7ed95a", 19 | "name": "Amelia", 20 | "email": "atondeur3@posterous.com" 21 | }, 22 | { 23 | "userId": "22c29051-d301-4cc2-86dd-c19695408adb", 24 | "name": "Bucky", 25 | "email": "btompkin4@moonfruit.com" 26 | }, 27 | { 28 | "userId": "6ac28937-bc05-4c0a-be7e-1a332e2de312", 29 | "name": "Sherline", 30 | "email": "sinston5@issuu.com" 31 | }, 32 | { 33 | "userId": "552c1c73-e324-47ee-bf7f-b0dfbeb59788", 34 | "name": "Leontine", 35 | "email": "lchartres6@edublogs.org" 36 | }, 37 | { 38 | "userId": "962c8a6b-c914-4aa4-93bc-2b91188a1a58", 39 | "name": "Cloris", 40 | "email": "cmorrall7@un.org" 41 | }, 42 | { 43 | "userId": "35393a7a-f41b-4fe9-8901-fc39a8f803d6", 44 | "name": "Tobiah", 45 | "email": "trubinchik8@time.com" 46 | }, 47 | { 48 | "userId": "9cf146a9-3da9-47fe-bcc3-3abdeb3a375d", 49 | "name": "Colet", 50 | "email": "cmincini9@dell.com" 51 | }, 52 | { 53 | "userId": "4a6efba9-61a2-4829-abe6-dfed18484737", 54 | "name": "Van", 55 | "email": "vswaitea@imdb.com" 56 | }, 57 | { 58 | "userId": "6718765e-123c-42d4-b2b3-efc029ff854e", 59 | "name": "Mella", 60 | "email": "mheartyb@sphinn.com" 61 | }, 62 | { 63 | "userId": "0880eb85-2a08-4898-8aae-3cf90c48b08b", 64 | "name": "Karyl", 65 | "email": "kmatteic@live.com" 66 | }, 67 | { 68 | "userId": "2977c5fe-22be-454a-80e1-5b93db92a371", 69 | "name": "Berrie", 70 | "email": "bnortcliffed@linkedin.com" 71 | }, 72 | { 73 | "userId": "bd909a0b-f665-451a-a052-8e8111e796e3", 74 | "name": "Giselle", 75 | "email": "gsollitte@weibo.com" 76 | }, 77 | { 78 | "userId": "26409ed7-15ac-4695-9813-be2afb6dad26", 79 | "name": "Niall", 80 | "email": "nrebeirof@netvibes.com" 81 | }, 82 | { 83 | "userId": "80697f6f-69bc-4b03-82c0-40f48884f716", 84 | "name": "Afton", 85 | "email": "ajozaitisg@craigslist.org" 86 | }, 87 | { 88 | "userId": "15d25fd9-32da-4ac7-b1b6-589a90a41dbf", 89 | "name": "Letisha", 90 | "email": "lgrimsdykeh@blogger.com" 91 | }, 92 | { 93 | "userId": "0bd8b2b2-d67f-47dc-9acc-d2311006852b", 94 | "name": "Julio", 95 | "email": "jcuniami@weibo.com" 96 | }, 97 | { 98 | "userId": "e83bae68-8104-4847-9f08-98206f35d100", 99 | "name": "Dana", 100 | "email": "dstrugnellj@51.la" 101 | }, 102 | { 103 | "userId": "a4cf8f1f-8c61-404d-834d-220202358f91", 104 | "name": "Gertie", 105 | "email": "gmacrok@networkadvertising.org" 106 | }, 107 | { 108 | "userId": "e091dbc4-0fc5-4823-9fc4-1cdded1fc5f4", 109 | "name": "Vidovik", 110 | "email": "vriddettl@usgs.gov" 111 | }, 112 | { 113 | "userId": "e9ceef74-fb81-41e0-b52e-0089b978b2f3", 114 | "name": "Yancey", 115 | "email": "yfentemm@51.la" 116 | }, 117 | { 118 | "userId": "9d82ca0e-cb41-4fba-a73f-acb7388e9d12", 119 | "name": "Lyndell", 120 | "email": "ldurninn@sciencedirect.com" 121 | }, 122 | { 123 | "userId": "ecea4d11-d41e-468a-85d3-e80a193a5620", 124 | "name": "Heidie", 125 | "email": "hrackhamo@craigslist.org" 126 | }, 127 | { 128 | "userId": "29232f0d-2423-406f-956b-00ddf9540ac8", 129 | "name": "Clem", 130 | "email": "cthorbonp@smugmug.com" 131 | }, 132 | { 133 | "userId": "afd4a67e-83ba-4a62-9cdc-2fdc0c553b29", 134 | "name": "Paten", 135 | "email": "pblasdaleq@quantcast.com" 136 | }, 137 | { 138 | "userId": "2a26982f-498f-4599-ab54-bc7469e2fbfd", 139 | "name": "Daisi", 140 | "email": "dsedgwickr@addthis.com" 141 | }, 142 | { 143 | "userId": "c876a2cc-7528-4b61-837b-b7f7efc62cca", 144 | "name": "Sara-ann", 145 | "email": "sblundels@csmonitor.com" 146 | } 147 | ] 148 | -------------------------------------------------------------------------------- /client/src/app/(components)/Sidebar/index.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useAppDispatch, useAppSelector } from "@/app/redux"; 4 | import { setIsSidebarCollapsed } from "@/state"; 5 | import { 6 | Archive, 7 | CircleDollarSign, 8 | Clipboard, 9 | Layout, 10 | LucideIcon, 11 | Menu, 12 | SlidersHorizontal, 13 | User, 14 | } from "lucide-react"; 15 | import Image from "next/image"; 16 | import Link from "next/link"; 17 | import { usePathname } from "next/navigation"; 18 | import React from "react"; 19 | 20 | interface SidebarLinkProps { 21 | href: string; 22 | icon: LucideIcon; 23 | label: string; 24 | isCollapsed: boolean; 25 | } 26 | 27 | const SidebarLink = ({ 28 | href, 29 | icon: Icon, 30 | label, 31 | isCollapsed, 32 | }: SidebarLinkProps) => { 33 | const pathname = usePathname(); 34 | const isActive = 35 | pathname === href || (pathname === "/" && href === "/dashboard"); 36 | 37 | return ( 38 | 39 |
48 | 49 | 50 | 55 | {label} 56 | 57 |
58 | 59 | ); 60 | }; 61 | 62 | const Sidebar = () => { 63 | const dispatch = useAppDispatch(); 64 | const isSidebarCollapsed = useAppSelector( 65 | (state) => state.global.isSidebarCollapsed 66 | ); 67 | 68 | const toggleSidebar = () => { 69 | dispatch(setIsSidebarCollapsed(!isSidebarCollapsed)); 70 | }; 71 | 72 | const sidebarClassNames = `fixed flex flex-col ${ 73 | isSidebarCollapsed ? "w-0 md:w-16" : "w-72 md:w-64" 74 | } bg-white transition-all duration-300 overflow-hidden h-full shadow-md z-40`; 75 | 76 | return ( 77 |
78 | {/* TOP LOGO */} 79 |
84 | edstock-logo 91 |

96 | EDSTOCK 97 |

98 | 99 | 105 |
106 | 107 | {/* LINKS */} 108 |
109 | 115 | 121 | 127 | 133 | 139 | 145 |
146 | 147 | {/* FOOTER */} 148 |
149 |

© 2024 Edstock

150 |
151 |
152 | ); 153 | }; 154 | 155 | export default Sidebar; 156 | -------------------------------------------------------------------------------- /client/src/app/dashboard/CardExpenseSummary.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | ExpenseByCategorySummary, 3 | useGetDashboardMetricsQuery, 4 | } from "@/state/api"; 5 | import { TrendingUp } from "lucide-react"; 6 | import { Cell, Pie, PieChart, ResponsiveContainer } from "recharts"; 7 | 8 | type ExpenseSums = { 9 | [category: string]: number; 10 | }; 11 | 12 | const colors = ["#00C49F", "#0088FE", "#FFBB28"]; 13 | 14 | const CardExpenseSummary = () => { 15 | const { data: dashboardMetrics, isLoading } = useGetDashboardMetricsQuery(); 16 | 17 | const expenseSummary = dashboardMetrics?.expenseSummary[0]; 18 | 19 | const expenseByCategorySummary = 20 | dashboardMetrics?.expenseByCategorySummary || []; 21 | 22 | const expenseSums = expenseByCategorySummary.reduce( 23 | (acc: ExpenseSums, item: ExpenseByCategorySummary) => { 24 | const category = item.category + " Expenses"; 25 | const amount = parseInt(item.amount, 10); 26 | if (!acc[category]) acc[category] = 0; 27 | acc[category] += amount; 28 | return acc; 29 | }, 30 | {} 31 | ); 32 | 33 | const expenseCategories = Object.entries(expenseSums).map( 34 | ([name, value]) => ({ 35 | name, 36 | value, 37 | }) 38 | ); 39 | 40 | const totalExpenses = expenseCategories.reduce( 41 | (acc, category: { value: number }) => acc + category.value, 42 | 0 43 | ); 44 | const formattedTotalExpenses = totalExpenses.toFixed(2); 45 | 46 | return ( 47 |
48 | {isLoading ? ( 49 |
Loading...
50 | ) : ( 51 | <> 52 | {/* HEADER */} 53 |
54 |

55 | Expense Summary 56 |

57 |
58 |
59 | {/* BODY */} 60 |
61 | {/* CHART */} 62 |
63 | 64 | 65 | 75 | {expenseCategories.map((entry, index) => ( 76 | 80 | ))} 81 | 82 | 83 | 84 |
85 | 86 | ${formattedTotalExpenses} 87 | 88 |
89 |
90 | {/* LABELS */} 91 |
    92 | {expenseCategories.map((entry, index) => ( 93 |
  • 97 | 101 | {entry.name} 102 |
  • 103 | ))} 104 |
105 |
106 | {/* FOOTER */} 107 |
108 |
109 | {expenseSummary && ( 110 |
111 |
112 |

113 | Average:{" "} 114 | 115 | ${expenseSummary.totalExpenses.toFixed(2)} 116 | 117 |

118 |
119 | 120 | 121 | 30% 122 | 123 |
124 | )} 125 |
126 | 127 | )} 128 |
129 | ); 130 | }; 131 | 132 | export default CardExpenseSummary; 133 | -------------------------------------------------------------------------------- /client/src/app/dashboard/CardSalesSummary.tsx: -------------------------------------------------------------------------------- 1 | import { useGetDashboardMetricsQuery } from "@/state/api"; 2 | import { TrendingUp } from "lucide-react"; 3 | import React, { useState } from "react"; 4 | import { 5 | Bar, 6 | BarChart, 7 | CartesianGrid, 8 | ResponsiveContainer, 9 | Tooltip, 10 | XAxis, 11 | YAxis, 12 | } from "recharts"; 13 | 14 | const CardSalesSummary = () => { 15 | const { data, isLoading, isError } = useGetDashboardMetricsQuery(); 16 | const salesData = data?.salesSummary || []; 17 | 18 | const [timeframe, setTimeframe] = useState("weekly"); 19 | 20 | const totalValueSum = 21 | salesData.reduce((acc, curr) => acc + curr.totalValue, 0) || 0; 22 | 23 | const averageChangePercentage = 24 | salesData.reduce((acc, curr, _, array) => { 25 | return acc + curr.changePercentage! / array.length; 26 | }, 0) || 0; 27 | 28 | const highestValueData = salesData.reduce((acc, curr) => { 29 | return acc.totalValue > curr.totalValue ? acc : curr; 30 | }, salesData[0] || {}); 31 | 32 | const highestValueDate = highestValueData.date 33 | ? new Date(highestValueData.date).toLocaleDateString("en-US", { 34 | month: "numeric", 35 | day: "numeric", 36 | year: "2-digit", 37 | }) 38 | : "N/A"; 39 | 40 | if (isError) { 41 | return
Failed to fetch data
; 42 | } 43 | 44 | return ( 45 |
46 | {isLoading ? ( 47 |
Loading...
48 | ) : ( 49 | <> 50 | {/* HEADER */} 51 |
52 |

53 | Sales Summary 54 |

55 |
56 |
57 | 58 | {/* BODY */} 59 |
60 | {/* BODY HEADER */} 61 |
62 |
63 |

Value

64 | 65 | $ 66 | {(totalValueSum / 1000000).toLocaleString("en", { 67 | maximumFractionDigits: 2, 68 | })} 69 | m 70 | 71 | 72 | 73 | {averageChangePercentage.toFixed(2)}% 74 | 75 |
76 | 87 |
88 | {/* CHART */} 89 | 90 | 94 | 95 | { 98 | const date = new Date(value); 99 | return `${date.getMonth() + 1}/${date.getDate()}`; 100 | }} 101 | /> 102 | { 104 | return `$${(value / 1000000).toFixed(0)}m`; 105 | }} 106 | tick={{ fontSize: 12, dx: -1 }} 107 | tickLine={false} 108 | axisLine={false} 109 | /> 110 | [ 112 | `$${value.toLocaleString("en")}`, 113 | ]} 114 | labelFormatter={(label) => { 115 | const date = new Date(label); 116 | return date.toLocaleDateString("en-US", { 117 | year: "numeric", 118 | month: "long", 119 | day: "numeric", 120 | }); 121 | }} 122 | /> 123 | 129 | 130 | 131 |
132 | 133 | {/* FOOTER */} 134 |
135 |
136 |
137 |

{salesData.length || 0} days

138 |

139 | Highest Sales Date:{" "} 140 | {highestValueDate} 141 |

142 |
143 |
144 | 145 | )} 146 |
147 | ); 148 | }; 149 | 150 | export default CardSalesSummary; 151 | -------------------------------------------------------------------------------- /server/prisma/seedData/purchaseSummary.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "purchaseSummaryId": "c452c304-0f7e-474f-b750-43c542df3412", 4 | "totalPurchased": 7599849.58, 5 | "changePercentage": -50.31, 6 | "date": "2023-05-27T05:20:14Z" 7 | }, 8 | { 9 | "purchaseSummaryId": "1206e83e-0e1b-42e5-8ec5-35cd1080f09d", 10 | "totalPurchased": 5701783.0, 11 | "changePercentage": -89.63, 12 | "date": "2023-12-05T05:52:50Z" 13 | }, 14 | { 15 | "purchaseSummaryId": "21491665-4761-4dea-b5f1-16b04d9f57bb", 16 | "totalPurchased": 2875081.56, 17 | "changePercentage": 97.28, 18 | "date": "2023-03-20T06:11:24Z" 19 | }, 20 | { 21 | "purchaseSummaryId": "a8ae9a24-a851-4a75-be59-a16834827bb8", 22 | "totalPurchased": 5887134.05, 23 | "changePercentage": 84.43, 24 | "date": "2023-12-16T17:22:23Z" 25 | }, 26 | { 27 | "purchaseSummaryId": "2de46932-fab9-41f6-87ca-8c06083a66f2", 28 | "totalPurchased": 6700381.67, 29 | "changePercentage": -15.4, 30 | "date": "2023-03-26T07:09:45Z" 31 | }, 32 | { 33 | "purchaseSummaryId": "63409896-65aa-4f06-9bb6-fd0b6336eeb5", 34 | "totalPurchased": 3703183.4, 35 | "changePercentage": 93.36, 36 | "date": "2023-09-12T18:48:33Z" 37 | }, 38 | { 39 | "purchaseSummaryId": "ab29178e-21a0-407f-8cef-8f99f5bd140c", 40 | "totalPurchased": 8183876.36, 41 | "changePercentage": 10.75, 42 | "date": "2023-07-13T19:13:53Z" 43 | }, 44 | { 45 | "purchaseSummaryId": "1475b96b-0d32-4679-b8dc-050e446f8cf8", 46 | "totalPurchased": 6771390.73, 47 | "changePercentage": 8.17, 48 | "date": "2023-04-17T20:13:19Z" 49 | }, 50 | { 51 | "purchaseSummaryId": "cd22c91d-af7a-4334-843c-0d9b3bd90bf8", 52 | "totalPurchased": 5362805.15, 53 | "changePercentage": -69.92, 54 | "date": "2023-12-30T11:00:31Z" 55 | }, 56 | { 57 | "purchaseSummaryId": "b9952c34-ef74-41f8-9c44-76e0516dc7b9", 58 | "totalPurchased": 7901696.36, 59 | "changePercentage": -8.22, 60 | "date": "2024-01-02T01:46:59Z" 61 | }, 62 | { 63 | "purchaseSummaryId": "677b220c-96b8-46b8-b367-be71b8a2fbb2", 64 | "totalPurchased": 592809.08, 65 | "changePercentage": -19.9, 66 | "date": "2023-07-07T19:23:16Z" 67 | }, 68 | { 69 | "purchaseSummaryId": "584fec60-a573-4445-b23e-fd9d46eebcab", 70 | "totalPurchased": 787656.13, 71 | "changePercentage": -85.81, 72 | "date": "2023-07-10T16:48:15Z" 73 | }, 74 | { 75 | "purchaseSummaryId": "124d1a24-3fd2-415d-9a6d-11737c319082", 76 | "totalPurchased": 1358410.16, 77 | "changePercentage": 81.52, 78 | "date": "2024-01-01T16:20:10Z" 79 | }, 80 | { 81 | "purchaseSummaryId": "fd8bfb7f-a97c-4dd3-a3b3-d838c6dcc753", 82 | "totalPurchased": 9262819.3, 83 | "changePercentage": -13.2, 84 | "date": "2024-01-08T04:29:17Z" 85 | }, 86 | { 87 | "purchaseSummaryId": "61d68449-e3ed-4fc7-b8f3-603c5c099ad0", 88 | "totalPurchased": 2892692.95, 89 | "changePercentage": 4.89, 90 | "date": "2023-07-24T18:44:29Z" 91 | }, 92 | { 93 | "purchaseSummaryId": "a9c42568-3668-4750-93a4-b8798471acde", 94 | "totalPurchased": 8176245.28, 95 | "changePercentage": -80.84, 96 | "date": "2024-02-07T16:49:38Z" 97 | }, 98 | { 99 | "purchaseSummaryId": "79d7b45a-ba98-437f-95b0-aa21c071709c", 100 | "totalPurchased": 6376329.63, 101 | "changePercentage": -17.35, 102 | "date": "2023-11-02T15:29:19Z" 103 | }, 104 | { 105 | "purchaseSummaryId": "71fe4534-87fc-439f-b6c8-3022aa5e7d4b", 106 | "totalPurchased": 490177.73, 107 | "changePercentage": -65.94, 108 | "date": "2023-08-25T13:45:04Z" 109 | }, 110 | { 111 | "purchaseSummaryId": "5c0a56c3-5140-447d-b992-a7d7c44d472d", 112 | "totalPurchased": 8033241.77, 113 | "changePercentage": -81.26, 114 | "date": "2023-11-16T18:22:18Z" 115 | }, 116 | { 117 | "purchaseSummaryId": "bd1cf049-4f69-4180-a5d1-72f37beef632", 118 | "totalPurchased": 8113334.96, 119 | "changePercentage": 18.99, 120 | "date": "2023-07-17T01:23:37Z" 121 | }, 122 | { 123 | "purchaseSummaryId": "6f24990f-336d-460e-84ad-810d159bad88", 124 | "totalPurchased": 2083304.59, 125 | "changePercentage": -80.93, 126 | "date": "2023-09-27T23:20:39Z" 127 | }, 128 | { 129 | "purchaseSummaryId": "37e2ae04-638f-41cc-b838-4f7ebdd5f60b", 130 | "totalPurchased": 6278248.14, 131 | "changePercentage": 8.44, 132 | "date": "2023-11-29T20:52:03Z" 133 | }, 134 | { 135 | "purchaseSummaryId": "a498409d-df59-46da-8065-ecaabc4e9225", 136 | "totalPurchased": 5013546.73, 137 | "changePercentage": -99.52, 138 | "date": "2023-08-01T12:30:43Z" 139 | }, 140 | { 141 | "purchaseSummaryId": "b1972f5c-50d3-4c8e-b3d2-d2ad12c86277", 142 | "totalPurchased": 7883690.1, 143 | "changePercentage": -95.76, 144 | "date": "2024-01-17T21:08:06Z" 145 | }, 146 | { 147 | "purchaseSummaryId": "d9d14c29-f07f-4cb5-8271-0330825e8159", 148 | "totalPurchased": 9402337.34, 149 | "changePercentage": 81.92, 150 | "date": "2024-01-21T00:14:36Z" 151 | }, 152 | { 153 | "purchaseSummaryId": "4fc51811-2e51-4776-b9b5-181e5f422f6a", 154 | "totalPurchased": 4571470.93, 155 | "changePercentage": -19.34, 156 | "date": "2023-06-01T18:39:59Z" 157 | }, 158 | { 159 | "purchaseSummaryId": "ccfd8bd5-c2a1-4d13-bec1-74157854773a", 160 | "totalPurchased": 4718798.68, 161 | "changePercentage": 95.35, 162 | "date": "2024-02-06T03:40:01Z" 163 | }, 164 | { 165 | "purchaseSummaryId": "c2ecb47d-7b26-4e78-8b48-96a436c434bd", 166 | "totalPurchased": 1087744.03, 167 | "changePercentage": 24.69, 168 | "date": "2024-01-28T09:04:52Z" 169 | }, 170 | { 171 | "purchaseSummaryId": "5fc29456-3bcc-4b2c-9096-3c36bcfae126", 172 | "totalPurchased": 3256718.55, 173 | "changePercentage": 68.1, 174 | "date": "2023-08-07T10:29:19Z" 175 | }, 176 | { 177 | "purchaseSummaryId": "3b42e538-f6b8-4d8b-8778-9c052a581d58", 178 | "totalPurchased": 826579.41, 179 | "changePercentage": -87.81, 180 | "date": "2023-11-19T02:00:54Z" 181 | } 182 | ] 183 | -------------------------------------------------------------------------------- /client/src/app/expenses/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { 4 | ExpenseByCategorySummary, 5 | useGetExpensesByCategoryQuery, 6 | } from "@/state/api"; 7 | import { useMemo, useState } from "react"; 8 | import Header from "@/app/(components)/Header"; 9 | import { 10 | Cell, 11 | Legend, 12 | Pie, 13 | PieChart, 14 | ResponsiveContainer, 15 | Tooltip, 16 | } from "recharts"; 17 | 18 | type AggregatedDataItem = { 19 | name: string; 20 | color?: string; 21 | amount: number; 22 | }; 23 | 24 | type AggregatedData = { 25 | [category: string]: AggregatedDataItem; 26 | }; 27 | 28 | const Expenses = () => { 29 | const [activeIndex, setActiveIndex] = useState(0); 30 | const [selectedCategory, setSelectedCategory] = useState("All"); 31 | const [startDate, setStartDate] = useState(""); 32 | const [endDate, setEndDate] = useState(""); 33 | 34 | const { 35 | data: expensesData, 36 | isLoading, 37 | isError, 38 | } = useGetExpensesByCategoryQuery(); 39 | const expenses = useMemo(() => expensesData ?? [], [expensesData]); 40 | 41 | const parseDate = (dateString: string) => { 42 | const date = new Date(dateString); 43 | return date.toISOString().split("T")[0]; 44 | }; 45 | 46 | const aggregatedData: AggregatedDataItem[] = useMemo(() => { 47 | const filtered: AggregatedData = expenses 48 | .filter((data: ExpenseByCategorySummary) => { 49 | const matchesCategory = 50 | selectedCategory === "All" || data.category === selectedCategory; 51 | const dataDate = parseDate(data.date); 52 | const matchesDate = 53 | !startDate || 54 | !endDate || 55 | (dataDate >= startDate && dataDate <= endDate); 56 | return matchesCategory && matchesDate; 57 | }) 58 | .reduce((acc: AggregatedData, data: ExpenseByCategorySummary) => { 59 | const amount = parseInt(data.amount); 60 | if (!acc[data.category]) { 61 | acc[data.category] = { name: data.category, amount: 0 }; 62 | acc[data.category].color = `#${Math.floor( 63 | Math.random() * 16777215 64 | ).toString(16)}`; 65 | acc[data.category].amount += amount; 66 | } 67 | return acc; 68 | }, {}); 69 | 70 | return Object.values(filtered); 71 | }, [expenses, selectedCategory, startDate, endDate]); 72 | 73 | const classNames = { 74 | label: "block text-sm font-medium text-gray-700", 75 | selectInput: 76 | "mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md", 77 | }; 78 | 79 | if (isLoading) { 80 | return
Loading...
; 81 | } 82 | 83 | if (isError || !expensesData) { 84 | return ( 85 |
86 | Failed to fetch expenses 87 |
88 | ); 89 | } 90 | 91 | return ( 92 |
93 | {/* HEADER */} 94 |
95 |
96 |

97 | A visual representation of expenses over time. 98 |

99 |
100 | 101 | {/* FILTERS */} 102 |
103 |
104 |

105 | Filter by Category and Date 106 |

107 |
108 | {/* CATEGORY */} 109 |
110 | 113 | 125 |
126 | {/* START DATE */} 127 |
128 | 131 | setStartDate(e.target.value)} 137 | /> 138 |
139 | {/* END DATE */} 140 |
141 | 144 | setEndDate(e.target.value)} 150 | /> 151 |
152 |
153 |
154 | {/* PIE CHART */} 155 |
156 | 157 | 158 | setActiveIndex(index)} 167 | > 168 | {aggregatedData.map( 169 | (entry: AggregatedDataItem, index: number) => ( 170 | 176 | ) 177 | )} 178 | 179 | 180 | 181 | 182 | 183 |
184 |
185 |
186 | ); 187 | }; 188 | 189 | export default Expenses; 190 | -------------------------------------------------------------------------------- /server/prisma/seedData/products.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "productId": "d35623ee-bef6-42b2-8776-2f99f8bb4782", 4 | "name": "Pinkscale Blazing Star", 5 | "price": 456.04, 6 | "rating": 2.25, 7 | "stockQuantity": 124834 8 | }, 9 | { 10 | "productId": "8ac1ac77-7358-425e-be16-0bdde9f02e59", 11 | "name": "Gila Milkvetch", 12 | "price": 899.05, 13 | "rating": 3.56, 14 | "stockQuantity": 799402 15 | }, 16 | { 17 | "productId": "1afc136b-4d9f-4e8e-aace-8e1df908a404", 18 | "name": "Rocky Mountain Zinnia", 19 | "price": 264.37, 20 | "rating": 3.23, 21 | "stockQuantity": 842192 22 | }, 23 | { 24 | "productId": "af84cc12-4fea-4f58-aece-f2ce92ca9580", 25 | "name": "Guadalupe Suncup", 26 | "price": 555.93, 27 | "rating": 4.09, 28 | "stockQuantity": 236333 29 | }, 30 | { 31 | "productId": "86e3bb1c-2f5d-4774-98f3-4df7cddd0a0f", 32 | "name": "Saline Phlox", 33 | "price": 82.62, 34 | "rating": 4.8, 35 | "stockQuantity": 601208 36 | }, 37 | { 38 | "productId": "26b017c6-06d8-443f-9b4a-d6b1cee6f4c0", 39 | "name": "Common Brighteyes", 40 | "price": 435.44, 41 | "rating": 0.27, 42 | "stockQuantity": 124068 43 | }, 44 | { 45 | "productId": "440c9e80-6bf8-4eb3-b2d2-f81936d67de3", 46 | "name": "Vermejo Phlox", 47 | "price": 759.15, 48 | "rating": 2.46, 49 | "stockQuantity": 234525 50 | }, 51 | { 52 | "productId": "98255f4e-40a6-470f-89a5-0792729f8947", 53 | "name": "Purple Marshlocks", 54 | "price": 974.99, 55 | "rating": 4.82, 56 | "stockQuantity": 739009 57 | }, 58 | { 59 | "productId": "2a339fb2-f9f3-43bc-a85a-b217a0a38f12", 60 | "name": "Hamatocaulis Moss", 61 | "price": 639.9, 62 | "rating": 1.17, 63 | "stockQuantity": 754285 64 | }, 65 | { 66 | "productId": "8a8391b2-b4ac-4847-b652-66ffd8d65875", 67 | "name": "Wax Myrtle", 68 | "price": 62.95, 69 | "rating": 4.6, 70 | "stockQuantity": 205240 71 | }, 72 | { 73 | "productId": "be2157fb-7454-405e-9511-bf7ba81b7726", 74 | "name": "Thladiantha", 75 | "price": 699.0, 76 | "rating": 1.65, 77 | "stockQuantity": 399124 78 | }, 79 | { 80 | "productId": "fdf1ba3d-fa06-4ce5-90ff-d081c5d37176", 81 | "name": "Common Tarweed", 82 | "price": 899.61, 83 | "rating": 2.39, 84 | "stockQuantity": 196884 85 | }, 86 | { 87 | "productId": "afded6df-058f-477d-9878-e0e0b1d3dff3", 88 | "name": "Smooth Phlox", 89 | "price": 575.6, 90 | "rating": 4.38, 91 | "stockQuantity": 673658 92 | }, 93 | { 94 | "productId": "daa29167-82a7-474b-9687-b8b903e7ec69", 95 | "name": "Lemmon's Beggarticks", 96 | "price": 492.35, 97 | "rating": 1.07, 98 | "stockQuantity": 205143 99 | }, 100 | { 101 | "productId": "ccb83982-71f3-4497-bad8-7e64c6920dc6", 102 | "name": "Globe Fimbry", 103 | "price": 304.69, 104 | "rating": 2.62, 105 | "stockQuantity": 388596 106 | }, 107 | { 108 | "productId": "1936d406-e89e-40e4-bff7-1827532269d4", 109 | "name": "Columbia Milkvetch", 110 | "price": 845.15, 111 | "rating": 2.21, 112 | "stockQuantity": 631658 113 | }, 114 | { 115 | "productId": "c849a535-5f8b-47e3-889c-015693a644ac", 116 | "name": "Girdlepod", 117 | "price": 880.09, 118 | "rating": 1.49, 119 | "stockQuantity": 65457 120 | }, 121 | { 122 | "productId": "0c3e80ee-59b3-4fc4-b760-8b07acc2d3ae", 123 | "name": "Lindley's Clerodendrum", 124 | "price": 51.66, 125 | "rating": 1.53, 126 | "stockQuantity": 263383 127 | }, 128 | { 129 | "productId": "d8f5bee3-f3eb-4071-a124-6b857e0fd798", 130 | "name": "Arizonia Dry Rock Moss", 131 | "price": 746.88, 132 | "rating": 4.71, 133 | "stockQuantity": 616812 134 | }, 135 | { 136 | "productId": "8d15de86-0e4a-4414-9166-7a33610202d3", 137 | "name": "Clamshell Orchid", 138 | "price": 17.1, 139 | "rating": 0.79, 140 | "stockQuantity": 604774 141 | }, 142 | { 143 | "productId": "ea8fd0b9-c2d9-4d43-9c23-44cb99d079bb", 144 | "name": "Fourleaf Mare's-tail", 145 | "price": 905.04, 146 | "rating": 3.71, 147 | "stockQuantity": 909107 148 | }, 149 | { 150 | "productId": "25d01c80-bca1-4a00-b1d0-0fbd39ff9e89", 151 | "name": "Simpson's Rosinweed", 152 | "price": 184.41, 153 | "rating": 1.98, 154 | "stockQuantity": 953695 155 | }, 156 | { 157 | "productId": "1d6df6e3-b7ea-4507-9d66-87c6ee8ed5b9", 158 | "name": "Lobelia", 159 | "price": 163.6, 160 | "rating": 0.81, 161 | "stockQuantity": 341530 162 | }, 163 | { 164 | "productId": "000a8c23-5bca-436c-a216-4e747a94c511", 165 | "name": "Yew Plum Pine", 166 | "price": 196.27, 167 | "rating": 1.6, 168 | "stockQuantity": 967173 169 | }, 170 | { 171 | "productId": "c5b600dc-6bfb-492a-b335-c3cc8c707959", 172 | "name": "Thimbleberry", 173 | "price": 602.37, 174 | "rating": 0.13, 175 | "stockQuantity": 162208 176 | }, 177 | { 178 | "productId": "9d5fafbc-312b-47e8-ada1-283918f0c3b5", 179 | "name": "Yellowturbans", 180 | "price": 564.82, 181 | "rating": 4.74, 182 | "stockQuantity": 33021 183 | }, 184 | { 185 | "productId": "0114d5d4-ae48-46fa-b0ca-afe60eb88add", 186 | "name": "Field Brome", 187 | "price": 664.2, 188 | "rating": 0.13, 189 | "stockQuantity": 363992 190 | }, 191 | { 192 | "productId": "e5b0da8c-148d-4680-b262-8609fb8a10da", 193 | "name": "Pentas", 194 | "price": 685.1, 195 | "rating": 1.5, 196 | "stockQuantity": 635092 197 | }, 198 | { 199 | "productId": "2be5b024-2c96-4f29-912c-c6f36353f799", 200 | "name": "Strigose Beard Lichen", 201 | "price": 373.81, 202 | "rating": 1.06, 203 | "stockQuantity": 35383 204 | }, 205 | { 206 | "productId": "fcf2e432-62a3-4b6f-a34d-36e42a12272e", 207 | "name": "Mad River Fleabane", 208 | "price": 669.97, 209 | "rating": 1.34, 210 | "stockQuantity": 880242 211 | }, 212 | { 213 | "productId": "fc4c81e5-f1ac-40f5-8c6f-da3fbad5599d", 214 | "name": "Chickenthief", 215 | "price": 100.11, 216 | "rating": 0.49, 217 | "stockQuantity": 896782 218 | }, 219 | { 220 | "productId": "07238d8e-0037-4972-87ca-0df206ee3e42", 221 | "name": "Palmleaf Poppymallow", 222 | "price": 22.99, 223 | "rating": 3.42, 224 | "stockQuantity": 635344 225 | }, 226 | { 227 | "productId": "154b7860-23a2-4564-ad99-1745ab7122ef", 228 | "name": "Guayanan Waterclover", 229 | "price": 45.45, 230 | "rating": 0.34, 231 | "stockQuantity": 456487 232 | }, 233 | { 234 | "productId": "8d4bf814-65d4-4df4-84cc-68911d925fdf", 235 | "name": "Emory's Acacia", 236 | "price": 847.6, 237 | "rating": 1.79, 238 | "stockQuantity": 638956 239 | }, 240 | { 241 | "productId": "a52bf1bd-3d35-4cd2-849a-354e3952e2d2", 242 | "name": "American Century Plant", 243 | "price": 969.47, 244 | "rating": 3.66, 245 | "stockQuantity": 248630 246 | } 247 | ] 248 | -------------------------------------------------------------------------------- /server/prisma/seedData/expenseSummary.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "expenseSummaryId": "5229a14b-443b-4551-9d01-4bc0dc820d05", 4 | "totalExpenses": 40749250.15, 5 | "date": "2022-08-23T23:59:13Z" 6 | }, 7 | { 8 | "expenseSummaryId": "c17ff164-1ab4-41e7-91a8-8aab790dcd88", 9 | "totalExpenses": 82516685.25, 10 | "date": "2021-12-28T17:03:47Z" 11 | }, 12 | { 13 | "expenseSummaryId": "45ecf33b-6c3b-4e55-84ec-fa30b88aa03f", 14 | "totalExpenses": 41860250.4, 15 | "date": "2021-09-18T23:26:30Z" 16 | }, 17 | { 18 | "expenseSummaryId": "e495b850-cf2b-4b76-93c2-d4241c859cd0", 19 | "totalExpenses": 49456718.68, 20 | "date": "2022-01-08T04:26:56Z" 21 | }, 22 | { 23 | "expenseSummaryId": "376f8d90-8b66-4cff-bd4e-2fcd14a42845", 24 | "totalExpenses": 35573786.54, 25 | "date": "2020-08-31T11:45:23Z" 26 | }, 27 | { 28 | "expenseSummaryId": "744bb795-0f98-45cd-99ae-45dddb99c3e4", 29 | "totalExpenses": 43767088.74, 30 | "date": "2020-04-15T06:57:51Z" 31 | }, 32 | { 33 | "expenseSummaryId": "4a6a10e5-4f44-4aef-a1bd-c57075f3c44f", 34 | "totalExpenses": 75311886.34, 35 | "date": "2021-11-05T16:10:48Z" 36 | }, 37 | { 38 | "expenseSummaryId": "d62a1123-18b3-4784-bbdd-70a29f773f99", 39 | "totalExpenses": 25031430.77, 40 | "date": "2020-11-18T23:52:46Z" 41 | }, 42 | { 43 | "expenseSummaryId": "ace2b05e-31de-48bd-a179-e59d16757456", 44 | "totalExpenses": 95066201.16, 45 | "date": "2020-05-10T17:55:37Z" 46 | }, 47 | { 48 | "expenseSummaryId": "f24f6dd9-dd18-42f9-a76a-95d8198b7636", 49 | "totalExpenses": 43519955.41, 50 | "date": "2022-02-18T23:26:53Z" 51 | }, 52 | { 53 | "expenseSummaryId": "2a2a88f0-0148-4667-8bab-7d129586bdf7", 54 | "totalExpenses": 11975718.82, 55 | "date": "2022-01-10T03:25:06Z" 56 | }, 57 | { 58 | "expenseSummaryId": "560c166c-7093-4583-a007-5af2a88f1dab", 59 | "totalExpenses": 51372909.22, 60 | "date": "2022-04-24T18:53:13Z" 61 | }, 62 | { 63 | "expenseSummaryId": "6faadd8d-5cdd-4b35-a3e8-cb0ada67de3a", 64 | "totalExpenses": 74722976.97, 65 | "date": "2022-01-14T09:36:38Z" 66 | }, 67 | { 68 | "expenseSummaryId": "be4755fd-0f7c-4292-a0ad-539ad1371f6e", 69 | "totalExpenses": 75641467.74, 70 | "date": "2021-12-21T15:01:09Z" 71 | }, 72 | { 73 | "expenseSummaryId": "46a33080-d239-49b2-9f0a-5d2d576a3966", 74 | "totalExpenses": 25500681.97, 75 | "date": "2022-04-01T15:50:39Z" 76 | }, 77 | { 78 | "expenseSummaryId": "40dd45c1-b575-48d1-b1a0-2bf474c8b591", 79 | "totalExpenses": 539510.64, 80 | "date": "2020-01-13T13:28:34Z" 81 | }, 82 | { 83 | "expenseSummaryId": "4d452c32-21fd-4891-a5fe-aa481861e0ad", 84 | "totalExpenses": 39145989.15, 85 | "date": "2022-04-13T22:53:06Z" 86 | }, 87 | { 88 | "expenseSummaryId": "acd41481-9384-4ff1-8004-a3df027a0209", 89 | "totalExpenses": 21149322.36, 90 | "date": "2022-03-11T04:03:13Z" 91 | }, 92 | { 93 | "expenseSummaryId": "9b523e04-fdae-4b0b-a6d9-58ef64c75431", 94 | "totalExpenses": 11169032.82, 95 | "date": "2021-08-06T01:20:04Z" 96 | }, 97 | { 98 | "expenseSummaryId": "4ada8077-3799-4690-ae35-c552e4100b1a", 99 | "totalExpenses": 82993070.31, 100 | "date": "2021-08-03T10:58:02Z" 101 | }, 102 | { 103 | "expenseSummaryId": "f6041c02-7b2a-4de3-8aed-81b82528ffe4", 104 | "totalExpenses": 66908936.55, 105 | "date": "2020-08-05T18:45:02Z" 106 | }, 107 | { 108 | "expenseSummaryId": "d68b89d4-3332-4e29-8e5a-3c0483af6688", 109 | "totalExpenses": 33514872.87, 110 | "date": "2021-07-01T15:13:37Z" 111 | }, 112 | { 113 | "expenseSummaryId": "9ef8296f-f2b3-4733-8664-7ef5e388734b", 114 | "totalExpenses": 63157720.91, 115 | "date": "2020-04-20T11:27:14Z" 116 | }, 117 | { 118 | "expenseSummaryId": "c9a46c10-c426-4cbe-b805-83ab5d963e19", 119 | "totalExpenses": 58355575.44, 120 | "date": "2022-02-17T22:27:44Z" 121 | }, 122 | { 123 | "expenseSummaryId": "8f70eaa2-fb78-4b32-bf2f-67c1e4988d2f", 124 | "totalExpenses": 84500652.59, 125 | "date": "2021-01-14T21:41:26Z" 126 | }, 127 | { 128 | "expenseSummaryId": "f222583a-d003-44e1-920d-0f9743a873dc", 129 | "totalExpenses": 83208591.41, 130 | "date": "2021-05-23T02:25:54Z" 131 | }, 132 | { 133 | "expenseSummaryId": "0675c624-1e11-4df8-821f-9a2d744dba09", 134 | "totalExpenses": 33465485.39, 135 | "date": "2022-10-29T12:01:44Z" 136 | }, 137 | { 138 | "expenseSummaryId": "abe1d0e1-65bf-49dc-801d-ba669aae8e33", 139 | "totalExpenses": 27097402.85, 140 | "date": "2021-01-02T11:54:11Z" 141 | }, 142 | { 143 | "expenseSummaryId": "6aef1c35-a1c4-4a58-9be0-b6488a622eaa", 144 | "totalExpenses": 60148771.5, 145 | "date": "2021-11-24T13:21:00Z" 146 | }, 147 | { 148 | "expenseSummaryId": "43b606a2-45c6-4da4-85aa-f219aa6a2960", 149 | "totalExpenses": 39634053.67, 150 | "date": "2021-01-30T15:52:39Z" 151 | }, 152 | { 153 | "expenseSummaryId": "db5b8de2-7dc0-4efe-a937-34e537a3f8ec", 154 | "totalExpenses": 63931774.54, 155 | "date": "2021-10-05T02:05:46Z" 156 | }, 157 | { 158 | "expenseSummaryId": "b0760c67-c326-4f6d-9511-cb2b6896a11f", 159 | "totalExpenses": 61688809.89, 160 | "date": "2022-07-02T22:34:16Z" 161 | }, 162 | { 163 | "expenseSummaryId": "8404fc43-72be-4a3a-bf55-313256b0e083", 164 | "totalExpenses": 70074829.75, 165 | "date": "2022-09-20T00:25:06Z" 166 | }, 167 | { 168 | "expenseSummaryId": "b149ef3b-f9cc-4560-ab29-4f74ec138c71", 169 | "totalExpenses": 80265893.3, 170 | "date": "2022-12-21T05:58:49Z" 171 | }, 172 | { 173 | "expenseSummaryId": "7e793e60-2edc-4adf-9d6f-bb6d09c93481", 174 | "totalExpenses": 10938282.95, 175 | "date": "2021-11-01T20:28:10Z" 176 | }, 177 | { 178 | "expenseSummaryId": "e56345be-2ac9-4ca7-a9a2-1c59846381f7", 179 | "totalExpenses": 33591951.95, 180 | "date": "2021-11-23T16:52:43Z" 181 | }, 182 | { 183 | "expenseSummaryId": "cdfd8bfd-1851-4cd1-ab5f-e66e7260ba92", 184 | "totalExpenses": 845358.59, 185 | "date": "2022-11-22T12:43:41Z" 186 | }, 187 | { 188 | "expenseSummaryId": "eb8f9ea8-2cdb-4461-80fc-9dd571afb200", 189 | "totalExpenses": 66690967.68, 190 | "date": "2022-02-13T23:10:27Z" 191 | }, 192 | { 193 | "expenseSummaryId": "1fe8f10b-24d0-4906-a66c-96ff7783671b", 194 | "totalExpenses": 43874777.57, 195 | "date": "2022-08-14T20:45:25Z" 196 | }, 197 | { 198 | "expenseSummaryId": "6cd41a74-4084-4e91-b009-5ab41730d258", 199 | "totalExpenses": 40265520.46, 200 | "date": "2022-07-16T21:42:02Z" 201 | }, 202 | { 203 | "expenseSummaryId": "d953877c-72d8-4189-8078-6ed4e46a23a4", 204 | "totalExpenses": 50581576.01, 205 | "date": "2020-07-23T13:00:44Z" 206 | }, 207 | { 208 | "expenseSummaryId": "e328c686-7e18-41b3-92e2-e2ddaf3a4a31", 209 | "totalExpenses": 68186405.58, 210 | "date": "2021-03-10T22:55:10Z" 211 | }, 212 | { 213 | "expenseSummaryId": "becde53e-60b5-40d1-be9c-27763057f3ac", 214 | "totalExpenses": 71676578.77, 215 | "date": "2022-10-17T04:27:37Z" 216 | }, 217 | { 218 | "expenseSummaryId": "803d384f-69a9-450d-8ed3-96d202dbafcd", 219 | "totalExpenses": 44651450.2, 220 | "date": "2020-08-08T19:52:19Z" 221 | }, 222 | { 223 | "expenseSummaryId": "4749833f-3eaa-452f-bb28-209988f7918d", 224 | "totalExpenses": 98571536.55, 225 | "date": "2022-06-18T07:04:03Z" 226 | }, 227 | { 228 | "expenseSummaryId": "852af507-0551-41c8-9d95-a90ed9b1a07c", 229 | "totalExpenses": 31396413.25, 230 | "date": "2021-09-18T17:44:00Z" 231 | }, 232 | { 233 | "expenseSummaryId": "45718dc4-41ef-4a92-b270-3f7eb445fbef", 234 | "totalExpenses": 30345484.18, 235 | "date": "2021-01-18T21:57:02Z" 236 | }, 237 | { 238 | "expenseSummaryId": "5763fb5e-a9b1-41a7-b873-0ecfad5c749c", 239 | "totalExpenses": 78841883.13, 240 | "date": "2021-01-10T19:15:27Z" 241 | }, 242 | { 243 | "expenseSummaryId": "87122213-3ad3-418a-8914-5a35e01bb69b", 244 | "totalExpenses": 1718619.1, 245 | "date": "2021-09-26T12:13:08Z" 246 | }, 247 | { 248 | "expenseSummaryId": "10ec2f80-6da0-4909-b8fd-7c5cfdb7bef4", 249 | "totalExpenses": 97167385.15, 250 | "date": "2020-03-04T02:21:16Z" 251 | } 252 | ] 253 | -------------------------------------------------------------------------------- /server/prisma/seedData/salesSummary.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "salesSummaryId": "9234a776-e6ac-46e2-bc24-c959ce216751", 4 | "totalValue": 4754106.83, 5 | "changePercentage": 61.51, 6 | "date": "2023-03-18T22:32:25Z" 7 | }, 8 | { 9 | "salesSummaryId": "e5648831-7d0e-4ef5-8e04-f6e6a0eaafb1", 10 | "totalValue": 1512948.97, 11 | "changePercentage": -2.28, 12 | "date": "2023-09-03T13:50:20Z" 13 | }, 14 | { 15 | "salesSummaryId": "785d33be-a1d8-47a6-b1d3-779942196b5c", 16 | "totalValue": 5545737.54, 17 | "changePercentage": -55.29, 18 | "date": "2023-07-28T13:16:27Z" 19 | }, 20 | { 21 | "salesSummaryId": "0541d262-46aa-4961-b7c0-ce09143ccf34", 22 | "totalValue": 3260113.92, 23 | "changePercentage": -71.7, 24 | "date": "2023-05-16T08:32:38Z" 25 | }, 26 | { 27 | "salesSummaryId": "185bb7e9-a9c0-4691-87d3-ca597a29e4d8", 28 | "totalValue": 849737.25, 29 | "changePercentage": 9.16, 30 | "date": "2023-08-26T05:41:40Z" 31 | }, 32 | { 33 | "salesSummaryId": "6a1cb0f7-a4e1-4157-800d-86a59fb5fc16", 34 | "totalValue": 98903.57, 35 | "changePercentage": 36.24, 36 | "date": "2023-09-02T01:49:46Z" 37 | }, 38 | { 39 | "salesSummaryId": "7d6d3e60-4687-40e3-9a77-452ea298df02", 40 | "totalValue": 973557.25, 41 | "changePercentage": 34.22, 42 | "date": "2023-03-31T15:20:53Z" 43 | }, 44 | { 45 | "salesSummaryId": "7e071f0f-cff2-4699-bc67-3bee1114cb9e", 46 | "totalValue": 9761085.56, 47 | "changePercentage": -57.32, 48 | "date": "2023-06-25T12:21:04Z" 49 | }, 50 | { 51 | "salesSummaryId": "6688c13c-758a-44c9-a291-d630d13dfd33", 52 | "totalValue": 9819343.72, 53 | "changePercentage": -49.57, 54 | "date": "2023-06-21T17:57:01Z" 55 | }, 56 | { 57 | "salesSummaryId": "bce35149-3c37-4a0d-8963-c9f550b262f3", 58 | "totalValue": 2757578.95, 59 | "changePercentage": 73.95, 60 | "date": "2023-05-13T15:02:46Z" 61 | }, 62 | { 63 | "salesSummaryId": "4d86c2df-d759-49df-9ecd-1eaf27a3d590", 64 | "totalValue": 8894817.67, 65 | "changePercentage": -25.81, 66 | "date": "2023-03-31T16:04:25Z" 67 | }, 68 | { 69 | "salesSummaryId": "c85efa84-d294-4c2e-a9a5-8774d92af8bf", 70 | "totalValue": 2882180.14, 71 | "changePercentage": 57.29, 72 | "date": "2024-03-13T01:19:11Z" 73 | }, 74 | { 75 | "salesSummaryId": "9257a2eb-d1ba-4cde-a0c2-cdf766c8c79c", 76 | "totalValue": 543716.99, 77 | "changePercentage": 8.85, 78 | "date": "2024-02-04T05:14:46Z" 79 | }, 80 | { 81 | "salesSummaryId": "2f94d909-0f3a-45fb-8072-5054b6dba2d6", 82 | "totalValue": 1171786.52, 83 | "changePercentage": 79.4, 84 | "date": "2023-04-20T00:12:05Z" 85 | }, 86 | { 87 | "salesSummaryId": "9bc6eca5-9f18-4e72-bed0-7a98bb759af3", 88 | "totalValue": 9574424.72, 89 | "changePercentage": 10.42, 90 | "date": "2023-04-04T22:57:17Z" 91 | }, 92 | { 93 | "salesSummaryId": "a0979a0f-bbe2-4bd8-9639-e6c1d890c6e1", 94 | "totalValue": 1717855.75, 95 | "changePercentage": -76.77, 96 | "date": "2023-07-24T23:01:04Z" 97 | }, 98 | { 99 | "salesSummaryId": "d2ff270c-63d9-4510-9524-91cb95494a9d", 100 | "totalValue": 1328587.6, 101 | "changePercentage": 62.35, 102 | "date": "2023-12-30T03:52:12Z" 103 | }, 104 | { 105 | "salesSummaryId": "56693648-d829-4d6a-8ff1-379ed00187c0", 106 | "totalValue": 3363438.49, 107 | "changePercentage": -3.73, 108 | "date": "2023-06-16T12:55:40Z" 109 | }, 110 | { 111 | "salesSummaryId": "22c58913-c4ad-44ac-bcf0-6309b6c61f26", 112 | "totalValue": 6253195.27, 113 | "changePercentage": -33.39, 114 | "date": "2023-05-12T13:22:28Z" 115 | }, 116 | { 117 | "salesSummaryId": "e14f2cdc-28c8-4041-a5c3-444e32f4df96", 118 | "totalValue": 759235.03, 119 | "changePercentage": -70.16, 120 | "date": "2023-05-04T03:54:06Z" 121 | }, 122 | { 123 | "salesSummaryId": "abb07538-994f-40df-9850-b93d758566d6", 124 | "totalValue": 8849902.08, 125 | "changePercentage": 49.99, 126 | "date": "2023-10-23T10:52:58Z" 127 | }, 128 | { 129 | "salesSummaryId": "3c1be92e-86a4-4ba9-9cc4-0cd25cdd9b53", 130 | "totalValue": 6985354.53, 131 | "changePercentage": -45.28, 132 | "date": "2023-06-12T08:59:54Z" 133 | }, 134 | { 135 | "salesSummaryId": "b6438519-cd43-49e4-a6ea-c1e97b6b9f4f", 136 | "totalValue": 1977818.88, 137 | "changePercentage": -1.73, 138 | "date": "2023-07-30T18:47:25Z" 139 | }, 140 | { 141 | "salesSummaryId": "47d22ba7-a75b-4570-a0ee-5936af301dc0", 142 | "totalValue": 3051711.61, 143 | "changePercentage": 46.06, 144 | "date": "2023-06-21T19:26:53Z" 145 | }, 146 | { 147 | "salesSummaryId": "75bf9bb7-67bf-4674-8d57-ef96c387bd5f", 148 | "totalValue": 1776483.92, 149 | "changePercentage": 5.92, 150 | "date": "2024-01-04T23:05:09Z" 151 | }, 152 | { 153 | "salesSummaryId": "aa076cf0-2af4-42d1-a65a-e21048900cdc", 154 | "totalValue": 8008789.18, 155 | "changePercentage": -92.62, 156 | "date": "2023-07-11T05:46:12Z" 157 | }, 158 | { 159 | "salesSummaryId": "0eabb55c-311b-4794-8621-684b8e3c6af3", 160 | "totalValue": 9939857.2, 161 | "changePercentage": 43.61, 162 | "date": "2023-11-03T17:55:50Z" 163 | }, 164 | { 165 | "salesSummaryId": "02421d34-eab8-4c74-be90-29ae960217e0", 166 | "totalValue": 7378147.37, 167 | "changePercentage": -8.68, 168 | "date": "2023-06-02T21:55:25Z" 169 | }, 170 | { 171 | "salesSummaryId": "dab41155-3b2c-4260-9b92-0fb36239e76a", 172 | "totalValue": 5903962.21, 173 | "changePercentage": 54.41, 174 | "date": "2023-04-15T12:08:49Z" 175 | }, 176 | { 177 | "salesSummaryId": "777946fe-c45b-48c4-8009-dd34727a2d6e", 178 | "totalValue": 3995392.55, 179 | "changePercentage": -39.88, 180 | "date": "2023-06-04T10:27:19Z" 181 | }, 182 | { 183 | "salesSummaryId": "0218422f-dff4-4b96-a485-ec81dfb52b1d", 184 | "totalValue": 2236665.35, 185 | "changePercentage": 62.25, 186 | "date": "2023-04-26T18:00:10Z" 187 | }, 188 | { 189 | "salesSummaryId": "2cdc1dff-3f48-4223-b75d-6d40d5ebd70f", 190 | "totalValue": 4924895.6, 191 | "changePercentage": 91.7, 192 | "date": "2023-10-03T09:22:11Z" 193 | }, 194 | { 195 | "salesSummaryId": "2876f4ae-7146-4144-a424-1050d3889af9", 196 | "totalValue": 8020749.83, 197 | "changePercentage": -53.71, 198 | "date": "2023-09-09T18:08:04Z" 199 | }, 200 | { 201 | "salesSummaryId": "72d4764c-6438-43e8-9f5e-1e1392c49daa", 202 | "totalValue": 1401814.98, 203 | "changePercentage": -62.89, 204 | "date": "2024-01-23T21:18:15Z" 205 | }, 206 | { 207 | "salesSummaryId": "0eb49bc9-7bb1-4593-b63c-623604b4d39e", 208 | "totalValue": 7075340.95, 209 | "changePercentage": 51.63, 210 | "date": "2023-04-12T18:35:19Z" 211 | }, 212 | { 213 | "salesSummaryId": "a03badbe-ed85-4f67-8563-e70957d711f5", 214 | "totalValue": 6635158.49, 215 | "changePercentage": 1.55, 216 | "date": "2024-01-08T18:20:24Z" 217 | }, 218 | { 219 | "salesSummaryId": "ef37f796-5792-48b5-947b-0861d1bcf1d2", 220 | "totalValue": 5438733.13, 221 | "changePercentage": 64.23, 222 | "date": "2023-06-04T03:34:31Z" 223 | }, 224 | { 225 | "salesSummaryId": "38f4698c-f973-4118-a38f-0c772dc55993", 226 | "totalValue": 8733498.2, 227 | "changePercentage": -9.14, 228 | "date": "2023-06-18T12:45:24Z" 229 | }, 230 | { 231 | "salesSummaryId": "bb7b3f86-95f6-414a-95c5-d6ad13a50e3b", 232 | "totalValue": 8834598.88, 233 | "changePercentage": -74.91, 234 | "date": "2023-09-18T22:57:29Z" 235 | }, 236 | { 237 | "salesSummaryId": "6f673a1d-78e9-4ea8-91e2-d7a32836cd3a", 238 | "totalValue": 1518126.03, 239 | "changePercentage": 22.59, 240 | "date": "2024-02-17T06:29:57Z" 241 | }, 242 | { 243 | "salesSummaryId": "ac08ecc4-f5e2-4f65-8f7d-3e9e02771657", 244 | "totalValue": 8916033.73, 245 | "changePercentage": -70.49, 246 | "date": "2023-11-23T06:52:42Z" 247 | }, 248 | { 249 | "salesSummaryId": "3245bcab-7939-43ef-8f75-66bb2e092637", 250 | "totalValue": 8457395.47, 251 | "changePercentage": 58.85, 252 | "date": "2023-06-14T06:58:44Z" 253 | }, 254 | { 255 | "salesSummaryId": "211ab48b-03e9-4e87-aff8-24723760c650", 256 | "totalValue": 2131348.79, 257 | "changePercentage": -46.72, 258 | "date": "2023-04-07T03:15:00Z" 259 | }, 260 | { 261 | "salesSummaryId": "6bff1b90-9e27-493b-aead-d70b388c5058", 262 | "totalValue": 4439655.91, 263 | "changePercentage": -10.54, 264 | "date": "2023-08-08T00:53:14Z" 265 | }, 266 | { 267 | "salesSummaryId": "b2cd84fc-8f66-477a-ad53-eb7826b89eae", 268 | "totalValue": 2253721.94, 269 | "changePercentage": -26.4, 270 | "date": "2023-12-04T03:33:12Z" 271 | }, 272 | { 273 | "salesSummaryId": "db47a861-5062-49c5-92c1-0a8f13ec70b0", 274 | "totalValue": 3844322.3, 275 | "changePercentage": -23.92, 276 | "date": "2023-07-18T05:39:04Z" 277 | }, 278 | { 279 | "salesSummaryId": "47ab518f-ec0e-4793-8d3b-53780452e472", 280 | "totalValue": 3145456.83, 281 | "changePercentage": -80.96, 282 | "date": "2023-05-18T14:04:36Z" 283 | }, 284 | { 285 | "salesSummaryId": "2f255c3a-d024-4a49-9e3e-3ff4e529c362", 286 | "totalValue": 5041224.3, 287 | "changePercentage": 50.26, 288 | "date": "2023-08-15T23:43:31Z" 289 | }, 290 | { 291 | "salesSummaryId": "1344c490-9f30-4a09-8379-e26dc551599e", 292 | "totalValue": 8409410.21, 293 | "changePercentage": 27.52, 294 | "date": "2024-01-01T14:21:37Z" 295 | }, 296 | { 297 | "salesSummaryId": "f9e8bb8b-b267-49d9-b621-7f912b348e81", 298 | "totalValue": 4049054.53, 299 | "changePercentage": -9.75, 300 | "date": "2023-04-08T22:11:59Z" 301 | } 302 | ] 303 | -------------------------------------------------------------------------------- /server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "nodenext" /* Specify what module code is generated. */, 29 | // "rootDir": "./", /* Specify the root folder within your source files. */ 30 | "moduleResolution": "nodenext" /* Specify how TypeScript looks up a file from a given module specifier. */, 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 39 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 40 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 41 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 42 | "resolveJsonModule": true /* Enable importing .json files. */, 43 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 44 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 45 | 46 | /* JavaScript Support */ 47 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 48 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 49 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 50 | 51 | /* Emit */ 52 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 53 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 54 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 55 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 56 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 57 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 58 | "outDir": "./dist" /* Specify an output folder for all emitted files. */, 59 | // "removeComments": true, /* Disable emitting comments. */ 60 | // "noEmit": true, /* Disable emitting files from a compilation. */ 61 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 62 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 63 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 64 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 65 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 66 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 67 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 68 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 69 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 70 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 71 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 72 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 73 | 74 | /* Interop Constraints */ 75 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 76 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 77 | // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ 78 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 79 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, 80 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 81 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, 82 | 83 | /* Type Checking */ 84 | "strict": true /* Enable all strict type-checking options. */, 85 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 86 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 87 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 88 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 89 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 90 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 91 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 92 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 93 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 94 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 95 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 96 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 97 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 98 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 99 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 100 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 101 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 102 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 103 | 104 | /* Completeness */ 105 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 106 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /server/prisma/seedData/expenseByCategory.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "expenseByCategoryId": "0e980d1b-ae93-4a5a-b235-4dfea5f42860", 4 | "expenseSummaryId": "5229a14b-443b-4551-9d01-4bc0dc820d05", 5 | "date": "2023-11-23T11:54:21Z", 6 | "category": "Office", 7 | "amount": 48 8 | }, 9 | { 10 | "expenseByCategoryId": "076a4956-60c2-4b73-b275-530359a91cba", 11 | "expenseSummaryId": "c17ff164-1ab4-41e7-91a8-8aab790dcd88", 12 | "date": "2023-08-13T09:49:33Z", 13 | "category": "Salaries", 14 | "amount": 66 15 | }, 16 | { 17 | "expenseByCategoryId": "27d49b13-1a3a-4fc7-924c-07147e4d08bd", 18 | "expenseSummaryId": "45ecf33b-6c3b-4e55-84ec-fa30b88aa03f", 19 | "date": "2023-07-24T09:26:56Z", 20 | "category": "Salaries", 21 | "amount": 63 22 | }, 23 | { 24 | "expenseByCategoryId": "037fce5f-2c74-4f7a-a863-8cdd4db8a56b", 25 | "expenseSummaryId": "e495b850-cf2b-4b76-93c2-d4241c859cd0", 26 | "date": "2023-05-24T05:24:15Z", 27 | "category": "Salaries", 28 | "amount": 77 29 | }, 30 | { 31 | "expenseByCategoryId": "abdb6c33-81ff-45dd-a653-ceae0783729d", 32 | "expenseSummaryId": "376f8d90-8b66-4cff-bd4e-2fcd14a42845", 33 | "date": "2023-05-07T12:33:09Z", 34 | "category": "Office", 35 | "amount": 67 36 | }, 37 | { 38 | "expenseByCategoryId": "246326ac-b8b2-44ee-9f14-f54086aa6a00", 39 | "expenseSummaryId": "744bb795-0f98-45cd-99ae-45dddb99c3e4", 40 | "date": "2024-01-08T22:08:30Z", 41 | "category": "Salaries", 42 | "amount": 12 43 | }, 44 | { 45 | "expenseByCategoryId": "760beed5-a14b-4e4b-9951-3ebe6f5842a1", 46 | "expenseSummaryId": "4a6a10e5-4f44-4aef-a1bd-c57075f3c44f", 47 | "date": "2023-06-11T08:03:25Z", 48 | "category": "Office", 49 | "amount": 57 50 | }, 51 | { 52 | "expenseByCategoryId": "6c653ab9-2b4b-49be-b891-944f5ff98b85", 53 | "expenseSummaryId": "d62a1123-18b3-4784-bbdd-70a29f773f99", 54 | "date": "2023-07-10T00:15:57Z", 55 | "category": "Office", 56 | "amount": 34 57 | }, 58 | { 59 | "expenseByCategoryId": "0636f954-6ef8-40b4-819f-fbfe4511be56", 60 | "expenseSummaryId": "ace2b05e-31de-48bd-a179-e59d16757456", 61 | "date": "2023-12-16T19:29:15Z", 62 | "category": "Salaries", 63 | "amount": 43 64 | }, 65 | { 66 | "expenseByCategoryId": "86f616b9-3d68-4a04-b8e8-966a11ce1a78", 67 | "expenseSummaryId": "f24f6dd9-dd18-42f9-a76a-95d8198b7636", 68 | "date": "2023-07-04T04:49:15Z", 69 | "category": "Salaries", 70 | "amount": 62 71 | }, 72 | { 73 | "expenseByCategoryId": "0381f4e7-08dc-4e10-8d8d-be02a3d1c16b", 74 | "expenseSummaryId": "2a2a88f0-0148-4667-8bab-7d129586bdf7", 75 | "date": "2023-10-18T18:41:07Z", 76 | "category": "Professional", 77 | "amount": 28 78 | }, 79 | { 80 | "expenseByCategoryId": "55628026-8104-4d95-91f8-8062df421036", 81 | "expenseSummaryId": "560c166c-7093-4583-a007-5af2a88f1dab", 82 | "date": "2024-01-01T03:49:45Z", 83 | "category": "Office", 84 | "amount": 65 85 | }, 86 | { 87 | "expenseByCategoryId": "44e1e4bc-462a-4b41-a1d2-a9997fa38ff8", 88 | "expenseSummaryId": "6faadd8d-5cdd-4b35-a3e8-cb0ada67de3a", 89 | "date": "2023-12-23T19:25:39Z", 90 | "category": "Salaries", 91 | "amount": 54 92 | }, 93 | { 94 | "expenseByCategoryId": "ab27a324-088c-4bdc-8ce4-c67a6fbff088", 95 | "expenseSummaryId": "be4755fd-0f7c-4292-a0ad-539ad1371f6e", 96 | "date": "2023-03-15T12:50:41Z", 97 | "category": "Salaries", 98 | "amount": 97 99 | }, 100 | { 101 | "expenseByCategoryId": "2e335aef-bb2e-401d-bda9-2c123e3031ad", 102 | "expenseSummaryId": "46a33080-d239-49b2-9f0a-5d2d576a3966", 103 | "date": "2023-04-23T21:27:57Z", 104 | "category": "Professional", 105 | "amount": 94 106 | }, 107 | { 108 | "expenseByCategoryId": "5cdabf88-3c50-4519-bcc3-f9eb2abcfdc9", 109 | "expenseSummaryId": "40dd45c1-b575-48d1-b1a0-2bf474c8b591", 110 | "date": "2023-05-11T09:55:13Z", 111 | "category": "Salaries", 112 | "amount": 24 113 | }, 114 | { 115 | "expenseByCategoryId": "bf128fb0-7c2f-45af-916e-4210d0b2c737", 116 | "expenseSummaryId": "4d452c32-21fd-4891-a5fe-aa481861e0ad", 117 | "date": "2023-06-16T16:48:42Z", 118 | "category": "Office", 119 | "amount": 59 120 | }, 121 | { 122 | "expenseByCategoryId": "70157f83-2d78-43b0-85c0-9c955abdbbcd", 123 | "expenseSummaryId": "acd41481-9384-4ff1-8004-a3df027a0209", 124 | "date": "2023-09-30T05:02:44Z", 125 | "category": "Office", 126 | "amount": 54 127 | }, 128 | { 129 | "expenseByCategoryId": "2f0fc0ff-11d6-4fa2-b930-84c1503fc4b6", 130 | "expenseSummaryId": "9b523e04-fdae-4b0b-a6d9-58ef64c75431", 131 | "date": "2023-07-03T22:12:16Z", 132 | "category": "Office", 133 | "amount": 25 134 | }, 135 | { 136 | "expenseByCategoryId": "ce4b9e25-25fa-49ed-8711-0ec432e913e3", 137 | "expenseSummaryId": "4ada8077-3799-4690-ae35-c552e4100b1a", 138 | "date": "2023-08-23T11:48:59Z", 139 | "category": "Professional", 140 | "amount": 54 141 | }, 142 | { 143 | "expenseByCategoryId": "4bc92339-cf13-4da5-bfc5-d201b4bce01f", 144 | "expenseSummaryId": "f6041c02-7b2a-4de3-8aed-81b82528ffe4", 145 | "date": "2023-09-14T06:41:32Z", 146 | "category": "Professional", 147 | "amount": 37 148 | }, 149 | { 150 | "expenseByCategoryId": "0cf47f96-e01f-443e-a864-9dd94aa0916b", 151 | "expenseSummaryId": "d68b89d4-3332-4e29-8e5a-3c0483af6688", 152 | "date": "2023-06-11T13:00:11Z", 153 | "category": "Office", 154 | "amount": 96 155 | }, 156 | { 157 | "expenseByCategoryId": "1629525c-9e25-4252-a55a-708eae10d62c", 158 | "expenseSummaryId": "9ef8296f-f2b3-4733-8664-7ef5e388734b", 159 | "date": "2024-01-03T17:10:35Z", 160 | "category": "Office", 161 | "amount": 85 162 | }, 163 | { 164 | "expenseByCategoryId": "33579bc1-739f-4489-9a5c-1fdea6e1dec2", 165 | "expenseSummaryId": "c9a46c10-c426-4cbe-b805-83ab5d963e19", 166 | "date": "2023-08-20T13:33:02Z", 167 | "category": "Salaries", 168 | "amount": 55 169 | }, 170 | { 171 | "expenseByCategoryId": "baf43dac-2563-4847-b756-a6c02cc17500", 172 | "expenseSummaryId": "8f70eaa2-fb78-4b32-bf2f-67c1e4988d2f", 173 | "date": "2023-12-23T06:16:20Z", 174 | "category": "Salaries", 175 | "amount": 7 176 | }, 177 | { 178 | "expenseByCategoryId": "c3784dc2-03aa-48c0-aaa7-54ab2d3fb049", 179 | "expenseSummaryId": "f222583a-d003-44e1-920d-0f9743a873dc", 180 | "date": "2023-09-20T10:01:41Z", 181 | "category": "Professional", 182 | "amount": 90 183 | }, 184 | { 185 | "expenseByCategoryId": "6e0372f9-0dba-4152-91f0-1c14f977b7a0", 186 | "expenseSummaryId": "0675c624-1e11-4df8-821f-9a2d744dba09", 187 | "date": "2023-09-02T16:42:29Z", 188 | "category": "Office", 189 | "amount": 99 190 | }, 191 | { 192 | "expenseByCategoryId": "c011c90e-ccfb-4dd8-b4b3-1daf483d9cba", 193 | "expenseSummaryId": "abe1d0e1-65bf-49dc-801d-ba669aae8e33", 194 | "date": "2023-07-05T08:02:52Z", 195 | "category": "Salaries", 196 | "amount": 52 197 | }, 198 | { 199 | "expenseByCategoryId": "2956f7b0-0526-4175-a596-7a8c22c4a6e1", 200 | "expenseSummaryId": "6aef1c35-a1c4-4a58-9be0-b6488a622eaa", 201 | "date": "2023-12-01T05:58:17Z", 202 | "category": "Professional", 203 | "amount": 71 204 | }, 205 | { 206 | "expenseByCategoryId": "2fc8c8c4-e1b3-4bc3-86af-ec2d83ed8511", 207 | "expenseSummaryId": "43b606a2-45c6-4da4-85aa-f219aa6a2960", 208 | "date": "2023-08-29T13:03:56Z", 209 | "category": "Salaries", 210 | "amount": 4 211 | }, 212 | { 213 | "expenseByCategoryId": "a4d61feb-528b-4314-8648-eb7c34c2303c", 214 | "expenseSummaryId": "db5b8de2-7dc0-4efe-a937-34e537a3f8ec", 215 | "date": "2023-09-24T19:24:50Z", 216 | "category": "Salaries", 217 | "amount": 7 218 | }, 219 | { 220 | "expenseByCategoryId": "b2a808b4-bba5-4091-a6b4-ce0f7b917807", 221 | "expenseSummaryId": "b0760c67-c326-4f6d-9511-cb2b6896a11f", 222 | "date": "2023-03-29T09:33:35Z", 223 | "category": "Office", 224 | "amount": 48 225 | }, 226 | { 227 | "expenseByCategoryId": "fcb238ba-dabc-4d93-9c34-aee8e305f866", 228 | "expenseSummaryId": "8404fc43-72be-4a3a-bf55-313256b0e083", 229 | "date": "2024-01-08T08:48:51Z", 230 | "category": "Professional", 231 | "amount": 15 232 | }, 233 | { 234 | "expenseByCategoryId": "4cfa1c7f-b18a-41d9-9e79-55854f1269bf", 235 | "expenseSummaryId": "b149ef3b-f9cc-4560-ab29-4f74ec138c71", 236 | "date": "2024-03-08T11:57:57Z", 237 | "category": "Professional", 238 | "amount": 94 239 | }, 240 | { 241 | "expenseByCategoryId": "a9f613be-6d9b-4ff0-8f26-37b71ed47274", 242 | "expenseSummaryId": "7e793e60-2edc-4adf-9d6f-bb6d09c93481", 243 | "date": "2024-02-15T10:50:32Z", 244 | "category": "Office", 245 | "amount": 94 246 | }, 247 | { 248 | "expenseByCategoryId": "198e6bd4-9586-47fd-903d-aac56f99b61d", 249 | "expenseSummaryId": "e56345be-2ac9-4ca7-a9a2-1c59846381f7", 250 | "date": "2023-08-13T13:25:33Z", 251 | "category": "Salaries", 252 | "amount": 10 253 | }, 254 | { 255 | "expenseByCategoryId": "9a86fea1-8b89-4f2f-829f-4406e530934d", 256 | "expenseSummaryId": "cdfd8bfd-1851-4cd1-ab5f-e66e7260ba92", 257 | "date": "2024-03-11T08:04:25Z", 258 | "category": "Office", 259 | "amount": 18 260 | }, 261 | { 262 | "expenseByCategoryId": "3ad489e8-ce80-4ace-b5e8-732d226affd2", 263 | "expenseSummaryId": "eb8f9ea8-2cdb-4461-80fc-9dd571afb200", 264 | "date": "2023-08-26T01:40:17Z", 265 | "category": "Salaries", 266 | "amount": 82 267 | }, 268 | { 269 | "expenseByCategoryId": "c8122a8a-498f-4504-97ed-e6361169099f", 270 | "expenseSummaryId": "1fe8f10b-24d0-4906-a66c-96ff7783671b", 271 | "date": "2024-02-03T20:08:28Z", 272 | "category": "Salaries", 273 | "amount": 95 274 | }, 275 | { 276 | "expenseByCategoryId": "fc7e618a-c26c-42e3-8e38-e87469127c7e", 277 | "expenseSummaryId": "6cd41a74-4084-4e91-b009-5ab41730d258", 278 | "date": "2023-10-07T22:01:48Z", 279 | "category": "Office", 280 | "amount": 91 281 | }, 282 | { 283 | "expenseByCategoryId": "a81be162-da70-43f0-a5d5-279f63bb1183", 284 | "expenseSummaryId": "d953877c-72d8-4189-8078-6ed4e46a23a4", 285 | "date": "2024-01-27T17:00:00Z", 286 | "category": "Salaries", 287 | "amount": 77 288 | }, 289 | { 290 | "expenseByCategoryId": "dac98685-6d90-41ed-80ba-dded515cc3f0", 291 | "expenseSummaryId": "e328c686-7e18-41b3-92e2-e2ddaf3a4a31", 292 | "date": "2023-07-03T02:00:59Z", 293 | "category": "Professional", 294 | "amount": 62 295 | }, 296 | { 297 | "expenseByCategoryId": "1241f3cf-99d8-4b84-b734-d60c3b53caad", 298 | "expenseSummaryId": "becde53e-60b5-40d1-be9c-27763057f3ac", 299 | "date": "2023-12-20T22:03:05Z", 300 | "category": "Salaries", 301 | "amount": 34 302 | }, 303 | { 304 | "expenseByCategoryId": "c8f652dd-e8a6-4eb5-bc17-993d58ea1e10", 305 | "expenseSummaryId": "803d384f-69a9-450d-8ed3-96d202dbafcd", 306 | "date": "2023-08-05T04:51:45Z", 307 | "category": "Salaries", 308 | "amount": 87 309 | }, 310 | { 311 | "expenseByCategoryId": "381c5e38-daae-473d-b32a-b1414106771a", 312 | "expenseSummaryId": "4749833f-3eaa-452f-bb28-209988f7918d", 313 | "date": "2024-01-15T07:35:22Z", 314 | "category": "Salaries", 315 | "amount": 12 316 | }, 317 | { 318 | "expenseByCategoryId": "96e7aac8-5c4e-4a14-9106-a31913e601d0", 319 | "expenseSummaryId": "852af507-0551-41c8-9d95-a90ed9b1a07c", 320 | "date": "2023-03-15T14:01:06Z", 321 | "category": "Salaries", 322 | "amount": 88 323 | }, 324 | { 325 | "expenseByCategoryId": "98ef7cce-68d9-4dee-bca0-804955502b83", 326 | "expenseSummaryId": "45718dc4-41ef-4a92-b270-3f7eb445fbef", 327 | "date": "2024-01-30T18:16:56Z", 328 | "category": "Salaries", 329 | "amount": 100 330 | }, 331 | { 332 | "expenseByCategoryId": "a8fbd7bf-b3f0-4f75-8b5d-6f0a078bef37", 333 | "expenseSummaryId": "5763fb5e-a9b1-41a7-b873-0ecfad5c749c", 334 | "date": "2023-12-20T11:34:30Z", 335 | "category": "Office", 336 | "amount": 98 337 | }, 338 | { 339 | "expenseByCategoryId": "5a34add7-30f1-4878-a0cc-e45556f5bfe9", 340 | "expenseSummaryId": "87122213-3ad3-418a-8914-5a35e01bb69b", 341 | "date": "2023-08-18T16:03:36Z", 342 | "category": "Professional", 343 | "amount": 78 344 | }, 345 | { 346 | "expenseByCategoryId": "1dac299c-9b5a-4fb4-8ab5-fcdf6f9f0298", 347 | "expenseSummaryId": "10ec2f80-6da0-4909-b8fd-7c5cfdb7bef4", 348 | "date": "2023-08-06T08:45:27Z", 349 | "category": "Office", 350 | "amount": 12 351 | } 352 | ] 353 | -------------------------------------------------------------------------------- /server/prisma/seedData/sales.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "saleId": "df8a03fa-c69c-4ffe-9e28-5febfb7a5ca9", 4 | "productId": "d35623ee-bef6-42b2-8776-2f99f8bb4782", 5 | "timestamp": "2023-11-02T04:03:00Z", 6 | "quantity": 229, 7 | "unitPrice": 221.1, 8 | "totalAmount": 8012.55 9 | }, 10 | { 11 | "saleId": "8d008094-bbb7-4b6f-a38c-0e2a9905322d", 12 | "productId": "8ac1ac77-7358-425e-be16-0bdde9f02e59", 13 | "timestamp": "2023-08-17T19:53:36Z", 14 | "quantity": 241, 15 | "unitPrice": 414.68, 16 | "totalAmount": 5661.25 17 | }, 18 | { 19 | "saleId": "4fca2797-3e88-40c5-88d3-fd4df8744958", 20 | "productId": "1afc136b-4d9f-4e8e-aace-8e1df908a404", 21 | "timestamp": "2023-11-21T15:03:18Z", 22 | "quantity": 699, 23 | "unitPrice": 550.22, 24 | "totalAmount": 4708.54 25 | }, 26 | { 27 | "saleId": "ae6a3d09-fdf8-407b-9e55-07a347c87efa", 28 | "productId": "af84cc12-4fea-4f58-aece-f2ce92ca9580", 29 | "timestamp": "2023-05-17T20:20:03Z", 30 | "quantity": 182, 31 | "unitPrice": 113.69, 32 | "totalAmount": 9419.4 33 | }, 34 | { 35 | "saleId": "f4dced2f-4446-450b-9778-fd32fe4dc583", 36 | "productId": "86e3bb1c-2f5d-4774-98f3-4df7cddd0a0f", 37 | "timestamp": "2023-06-14T15:17:34Z", 38 | "quantity": 863, 39 | "unitPrice": 214.01, 40 | "totalAmount": 8942.2 41 | }, 42 | { 43 | "saleId": "b9c0856c-d10f-47a1-bf05-2f2c400aff69", 44 | "productId": "26b017c6-06d8-443f-9b4a-d6b1cee6f4c0", 45 | "timestamp": "2023-10-27T18:59:51Z", 46 | "quantity": 985, 47 | "unitPrice": 506.74, 48 | "totalAmount": 1790.08 49 | }, 50 | { 51 | "saleId": "4411942c-1d3e-42b1-89bd-a692c43185b4", 52 | "productId": "440c9e80-6bf8-4eb3-b2d2-f81936d67de3", 53 | "timestamp": "2023-06-17T19:16:31Z", 54 | "quantity": 607, 55 | "unitPrice": 100.77, 56 | "totalAmount": 3293.73 57 | }, 58 | { 59 | "saleId": "85460f0a-83d5-45b5-8db0-42506e0c5739", 60 | "productId": "98255f4e-40a6-470f-89a5-0792729f8947", 61 | "timestamp": "2024-01-12T16:37:28Z", 62 | "quantity": 93, 63 | "unitPrice": 310.09, 64 | "totalAmount": 5531.81 65 | }, 66 | { 67 | "saleId": "1d9c4316-ed1a-49c5-a9ed-71a9fc8aff1c", 68 | "productId": "2a339fb2-f9f3-43bc-a85a-b217a0a38f12", 69 | "timestamp": "2023-11-08T18:16:52Z", 70 | "quantity": 482, 71 | "unitPrice": 83.62, 72 | "totalAmount": 894.41 73 | }, 74 | { 75 | "saleId": "94a6ad98-8efe-4aef-bf19-742a542cf790", 76 | "productId": "8a8391b2-b4ac-4847-b652-66ffd8d65875", 77 | "timestamp": "2023-12-21T22:49:11Z", 78 | "quantity": 285, 79 | "unitPrice": 76.09, 80 | "totalAmount": 3738.87 81 | }, 82 | { 83 | "saleId": "cff83ef4-6b2c-4ad4-b550-929e0eb3fa93", 84 | "productId": "be2157fb-7454-405e-9511-bf7ba81b7726", 85 | "timestamp": "2023-11-21T15:03:43Z", 86 | "quantity": 219, 87 | "unitPrice": 606.43, 88 | "totalAmount": 8773.57 89 | }, 90 | { 91 | "saleId": "bc6fbde5-325d-4918-b8d9-36d8a7be769f", 92 | "productId": "fdf1ba3d-fa06-4ce5-90ff-d081c5d37176", 93 | "timestamp": "2024-03-01T17:35:27Z", 94 | "quantity": 286, 95 | "unitPrice": 246.58, 96 | "totalAmount": 3328.18 97 | }, 98 | { 99 | "saleId": "022fe7a7-3938-4f41-9f56-f9e0a797f00f", 100 | "productId": "afded6df-058f-477d-9878-e0e0b1d3dff3", 101 | "timestamp": "2023-06-27T12:09:45Z", 102 | "quantity": 107, 103 | "unitPrice": 891.26, 104 | "totalAmount": 3915.37 105 | }, 106 | { 107 | "saleId": "e6fc26ef-50e8-4983-a7e0-f2cbf32d32ef", 108 | "productId": "daa29167-82a7-474b-9687-b8b903e7ec69", 109 | "timestamp": "2023-07-06T02:50:19Z", 110 | "quantity": 195, 111 | "unitPrice": 809.59, 112 | "totalAmount": 725.11 113 | }, 114 | { 115 | "saleId": "5ab2a0f1-2248-4804-9bf0-219d8a30b076", 116 | "productId": "ccb83982-71f3-4497-bad8-7e64c6920dc6", 117 | "timestamp": "2023-03-18T01:14:42Z", 118 | "quantity": 339, 119 | "unitPrice": 662.74, 120 | "totalAmount": 3694.96 121 | }, 122 | { 123 | "saleId": "a3659c0d-ccc6-4903-b035-6f13e7778253", 124 | "productId": "1936d406-e89e-40e4-bff7-1827532269d4", 125 | "timestamp": "2023-11-05T00:17:48Z", 126 | "quantity": 71, 127 | "unitPrice": 529.95, 128 | "totalAmount": 2535.36 129 | }, 130 | { 131 | "saleId": "169eacd5-c2e3-4510-a102-12d4b8351401", 132 | "productId": "c849a535-5f8b-47e3-889c-015693a644ac", 133 | "timestamp": "2023-11-03T18:34:59Z", 134 | "quantity": 235, 135 | "unitPrice": 769.19, 136 | "totalAmount": 7007.95 137 | }, 138 | { 139 | "saleId": "a2241026-9fbb-438d-8f74-186bfa7c3174", 140 | "productId": "0c3e80ee-59b3-4fc4-b760-8b07acc2d3ae", 141 | "timestamp": "2024-01-04T00:19:37Z", 142 | "quantity": 489, 143 | "unitPrice": 965.72, 144 | "totalAmount": 8759.91 145 | }, 146 | { 147 | "saleId": "6e1ef31f-9990-43d3-a8ba-54f535d450e9", 148 | "productId": "d8f5bee3-f3eb-4071-a124-6b857e0fd798", 149 | "timestamp": "2023-07-29T03:12:53Z", 150 | "quantity": 967, 151 | "unitPrice": 486.31, 152 | "totalAmount": 3167.8 153 | }, 154 | { 155 | "saleId": "ec369281-eefe-4a41-bcfc-f1f003ab8144", 156 | "productId": "8d15de86-0e4a-4414-9166-7a33610202d3", 157 | "timestamp": "2023-06-16T03:56:50Z", 158 | "quantity": 371, 159 | "unitPrice": 413.88, 160 | "totalAmount": 668.91 161 | }, 162 | { 163 | "saleId": "7f455867-3226-43c8-9838-091356e52573", 164 | "productId": "ea8fd0b9-c2d9-4d43-9c23-44cb99d079bb", 165 | "timestamp": "2023-12-03T20:33:41Z", 166 | "quantity": 435, 167 | "unitPrice": 477.74, 168 | "totalAmount": 9592.66 169 | }, 170 | { 171 | "saleId": "a3ee350e-5dee-4ce3-831f-710da0067756", 172 | "productId": "25d01c80-bca1-4a00-b1d0-0fbd39ff9e89", 173 | "timestamp": "2023-12-27T16:34:21Z", 174 | "quantity": 699, 175 | "unitPrice": 336.99, 176 | "totalAmount": 8316.35 177 | }, 178 | { 179 | "saleId": "c535a63e-376a-404e-b81e-4b448a06ac0e", 180 | "productId": "1d6df6e3-b7ea-4507-9d66-87c6ee8ed5b9", 181 | "timestamp": "2024-03-14T02:16:17Z", 182 | "quantity": 540, 183 | "unitPrice": 656.63, 184 | "totalAmount": 9777.17 185 | }, 186 | { 187 | "saleId": "b7d157e1-0931-4ebe-8ca3-571cee3b597c", 188 | "productId": "000a8c23-5bca-436c-a216-4e747a94c511", 189 | "timestamp": "2024-02-13T07:28:00Z", 190 | "quantity": 884, 191 | "unitPrice": 899.96, 192 | "totalAmount": 579.95 193 | }, 194 | { 195 | "saleId": "04ec2c14-ee2f-4485-9946-b154ed2df29a", 196 | "productId": "c5b600dc-6bfb-492a-b335-c3cc8c707959", 197 | "timestamp": "2023-09-04T23:51:27Z", 198 | "quantity": 106, 199 | "unitPrice": 272.72, 200 | "totalAmount": 8699.14 201 | }, 202 | { 203 | "saleId": "ca9f6794-b55e-4fb6-b76e-2e2e4d79d8f8", 204 | "productId": "9d5fafbc-312b-47e8-ada1-283918f0c3b5", 205 | "timestamp": "2023-06-27T17:57:01Z", 206 | "quantity": 543, 207 | "unitPrice": 15.84, 208 | "totalAmount": 304.87 209 | }, 210 | { 211 | "saleId": "2fc702a0-8959-4794-9847-c99da26a83c2", 212 | "productId": "0114d5d4-ae48-46fa-b0ca-afe60eb88add", 213 | "timestamp": "2023-03-27T00:06:48Z", 214 | "quantity": 262, 215 | "unitPrice": 838.85, 216 | "totalAmount": 5265.29 217 | }, 218 | { 219 | "saleId": "4d7f8590-2db0-4e05-9215-a5225eb99c1d", 220 | "productId": "e5b0da8c-148d-4680-b262-8609fb8a10da", 221 | "timestamp": "2023-09-25T03:45:29Z", 222 | "quantity": 485, 223 | "unitPrice": 268.43, 224 | "totalAmount": 5710.52 225 | }, 226 | { 227 | "saleId": "b3fb5092-8991-4f6d-8b01-05cc2fe624e5", 228 | "productId": "2be5b024-2c96-4f29-912c-c6f36353f799", 229 | "timestamp": "2023-04-01T01:39:53Z", 230 | "quantity": 561, 231 | "unitPrice": 783.41, 232 | "totalAmount": 8189.57 233 | }, 234 | { 235 | "saleId": "877c1b5b-d3d5-4f92-b640-f1d74e2207cd", 236 | "productId": "fcf2e432-62a3-4b6f-a34d-36e42a12272e", 237 | "timestamp": "2023-07-10T13:42:52Z", 238 | "quantity": 333, 239 | "unitPrice": 491.06, 240 | "totalAmount": 6152.84 241 | }, 242 | { 243 | "saleId": "58de1711-bf0c-4a7e-9045-06e8dae2fb20", 244 | "productId": "fc4c81e5-f1ac-40f5-8c6f-da3fbad5599d", 245 | "timestamp": "2023-09-06T15:15:47Z", 246 | "quantity": 949, 247 | "unitPrice": 120.98, 248 | "totalAmount": 8873.08 249 | }, 250 | { 251 | "saleId": "ee74af3c-ef81-45d2-a6e6-d883ff816992", 252 | "productId": "07238d8e-0037-4972-87ca-0df206ee3e42", 253 | "timestamp": "2024-03-05T13:54:29Z", 254 | "quantity": 2, 255 | "unitPrice": 234.17, 256 | "totalAmount": 7048.43 257 | }, 258 | { 259 | "saleId": "9c7317c6-39fa-4ab6-adb0-cde1a11c2f47", 260 | "productId": "154b7860-23a2-4564-ad99-1745ab7122ef", 261 | "timestamp": "2023-03-22T06:46:16Z", 262 | "quantity": 292, 263 | "unitPrice": 707.73, 264 | "totalAmount": 2045.56 265 | }, 266 | { 267 | "saleId": "5d5b341f-82c6-439a-8d59-44b38733cb7a", 268 | "productId": "8d4bf814-65d4-4df4-84cc-68911d925fdf", 269 | "timestamp": "2023-06-12T18:25:57Z", 270 | "quantity": 675, 271 | "unitPrice": 243.48, 272 | "totalAmount": 9259.03 273 | }, 274 | { 275 | "saleId": "d47eb898-5413-4492-ac54-cebb2422ebcf", 276 | "productId": "a52bf1bd-3d35-4cd2-849a-354e3952e2d2", 277 | "timestamp": "2023-11-21T03:27:09Z", 278 | "quantity": 7, 279 | "unitPrice": 44.74, 280 | "totalAmount": 9210.79 281 | }, 282 | { 283 | "saleId": "01ca19c0-c93a-464c-bc54-a14e9e80c55c", 284 | "productId": "d35623ee-bef6-42b2-8776-2f99f8bb4782", 285 | "timestamp": "2024-01-17T01:48:30Z", 286 | "quantity": 659, 287 | "unitPrice": 497.62, 288 | "totalAmount": 6661.77 289 | }, 290 | { 291 | "saleId": "9c03ee38-1424-4d47-bf27-4ad1441e0391", 292 | "productId": "8ac1ac77-7358-425e-be16-0bdde9f02e59", 293 | "timestamp": "2024-02-28T00:43:12Z", 294 | "quantity": 818, 295 | "unitPrice": 685.08, 296 | "totalAmount": 8503.33 297 | }, 298 | { 299 | "saleId": "55880ad4-1b25-4c67-a21a-9c2e480752af", 300 | "productId": "1afc136b-4d9f-4e8e-aace-8e1df908a404", 301 | "timestamp": "2023-09-05T17:54:43Z", 302 | "quantity": 578, 303 | "unitPrice": 817.99, 304 | "totalAmount": 1584.0 305 | }, 306 | { 307 | "saleId": "76515f08-ff03-46a5-8435-af7345ff9bab", 308 | "productId": "af84cc12-4fea-4f58-aece-f2ce92ca9580", 309 | "timestamp": "2023-08-19T00:33:46Z", 310 | "quantity": 491, 311 | "unitPrice": 801.15, 312 | "totalAmount": 3758.2 313 | }, 314 | { 315 | "saleId": "b84d883b-348a-4ea2-9f3b-7e9c6e841634", 316 | "productId": "86e3bb1c-2f5d-4774-98f3-4df7cddd0a0f", 317 | "timestamp": "2023-12-27T23:17:04Z", 318 | "quantity": 281, 319 | "unitPrice": 317.09, 320 | "totalAmount": 6151.55 321 | }, 322 | { 323 | "saleId": "2be4ca15-1cfb-4b06-a87a-3494b02dacab", 324 | "productId": "26b017c6-06d8-443f-9b4a-d6b1cee6f4c0", 325 | "timestamp": "2023-11-18T12:30:37Z", 326 | "quantity": 666, 327 | "unitPrice": 690.3, 328 | "totalAmount": 1522.01 329 | }, 330 | { 331 | "saleId": "3e1685c3-b8d1-49ac-8f00-15fb7f4e1a3d", 332 | "productId": "440c9e80-6bf8-4eb3-b2d2-f81936d67de3", 333 | "timestamp": "2024-01-22T17:28:27Z", 334 | "quantity": 113, 335 | "unitPrice": 54.35, 336 | "totalAmount": 8803.88 337 | }, 338 | { 339 | "saleId": "446fca08-cdec-43c3-afe8-2c9b82016062", 340 | "productId": "98255f4e-40a6-470f-89a5-0792729f8947", 341 | "timestamp": "2023-03-29T14:37:25Z", 342 | "quantity": 574, 343 | "unitPrice": 605.86, 344 | "totalAmount": 749.81 345 | }, 346 | { 347 | "saleId": "b84043c2-e60a-4c2f-9a10-6a56cedbb2a9", 348 | "productId": "2a339fb2-f9f3-43bc-a85a-b217a0a38f12", 349 | "timestamp": "2023-10-04T08:22:42Z", 350 | "quantity": 26, 351 | "unitPrice": 438.25, 352 | "totalAmount": 9407.62 353 | }, 354 | { 355 | "saleId": "ca781bd3-fa61-4f5b-bdc2-3053531a9d2f", 356 | "productId": "8a8391b2-b4ac-4847-b652-66ffd8d65875", 357 | "timestamp": "2024-02-12T03:24:52Z", 358 | "quantity": 452, 359 | "unitPrice": 511.0, 360 | "totalAmount": 8734.61 361 | }, 362 | { 363 | "saleId": "caa5f9b6-1458-4754-b0d1-19adebd562cf", 364 | "productId": "be2157fb-7454-405e-9511-bf7ba81b7726", 365 | "timestamp": "2023-12-25T10:27:27Z", 366 | "quantity": 484, 367 | "unitPrice": 280.73, 368 | "totalAmount": 5679.77 369 | }, 370 | { 371 | "saleId": "36a183a7-7abb-4f74-938b-b21801aa4c22", 372 | "productId": "fdf1ba3d-fa06-4ce5-90ff-d081c5d37176", 373 | "timestamp": "2023-08-16T06:01:27Z", 374 | "quantity": 260, 375 | "unitPrice": 232.89, 376 | "totalAmount": 3765.82 377 | }, 378 | { 379 | "saleId": "d62aaec8-35f2-44f2-ba46-5f024ddcb1c3", 380 | "productId": "afded6df-058f-477d-9878-e0e0b1d3dff3", 381 | "timestamp": "2023-10-02T16:42:02Z", 382 | "quantity": 718, 383 | "unitPrice": 685.31, 384 | "totalAmount": 6321.19 385 | }, 386 | { 387 | "saleId": "253c02a9-decd-4d11-bf40-9cf6d8c12562", 388 | "productId": "daa29167-82a7-474b-9687-b8b903e7ec69", 389 | "timestamp": "2023-06-10T02:42:28Z", 390 | "quantity": 297, 391 | "unitPrice": 953.45, 392 | "totalAmount": 2757.43 393 | }, 394 | { 395 | "saleId": "204bf1e8-c1ec-4c28-b209-4b3485983956", 396 | "productId": "ccb83982-71f3-4497-bad8-7e64c6920dc6", 397 | "timestamp": "2023-07-16T21:58:15Z", 398 | "quantity": 447, 399 | "unitPrice": 777.27, 400 | "totalAmount": 4574.02 401 | }, 402 | { 403 | "saleId": "8f2cc66d-1cee-4bdb-8d71-124f27c5de48", 404 | "productId": "1936d406-e89e-40e4-bff7-1827532269d4", 405 | "timestamp": "2023-06-17T10:37:35Z", 406 | "quantity": 390, 407 | "unitPrice": 855.29, 408 | "totalAmount": 4044.96 409 | }, 410 | { 411 | "saleId": "3a28dbfa-758c-4f0f-a086-0b04e7dd94a7", 412 | "productId": "c849a535-5f8b-47e3-889c-015693a644ac", 413 | "timestamp": "2024-01-31T06:04:28Z", 414 | "quantity": 851, 415 | "unitPrice": 170.23, 416 | "totalAmount": 9399.42 417 | }, 418 | { 419 | "saleId": "12bd3ad4-b486-4515-9192-c2c2a33f97ff", 420 | "productId": "0c3e80ee-59b3-4fc4-b760-8b07acc2d3ae", 421 | "timestamp": "2023-05-03T13:57:12Z", 422 | "quantity": 882, 423 | "unitPrice": 973.95, 424 | "totalAmount": 4241.28 425 | }, 426 | { 427 | "saleId": "d4fcfc21-9191-4419-a18d-a15282881274", 428 | "productId": "d8f5bee3-f3eb-4071-a124-6b857e0fd798", 429 | "timestamp": "2023-10-22T01:09:46Z", 430 | "quantity": 830, 431 | "unitPrice": 200.27, 432 | "totalAmount": 3059.84 433 | }, 434 | { 435 | "saleId": "00680af4-e08f-4360-bcb0-e5702edec5f9", 436 | "productId": "8d15de86-0e4a-4414-9166-7a33610202d3", 437 | "timestamp": "2023-05-09T15:02:48Z", 438 | "quantity": 414, 439 | "unitPrice": 924.09, 440 | "totalAmount": 8604.22 441 | }, 442 | { 443 | "saleId": "86b7e33b-e09e-4cdd-ac75-ad2d563936d9", 444 | "productId": "ea8fd0b9-c2d9-4d43-9c23-44cb99d079bb", 445 | "timestamp": "2023-03-23T16:14:23Z", 446 | "quantity": 390, 447 | "unitPrice": 792.89, 448 | "totalAmount": 6503.0 449 | }, 450 | { 451 | "saleId": "baf92b24-73f7-45f8-8326-80c7d151472e", 452 | "productId": "25d01c80-bca1-4a00-b1d0-0fbd39ff9e89", 453 | "timestamp": "2024-02-22T07:16:30Z", 454 | "quantity": 391, 455 | "unitPrice": 685.29, 456 | "totalAmount": 9900.96 457 | }, 458 | { 459 | "saleId": "dc8df228-0241-4cc0-95ee-3b5eda831524", 460 | "productId": "1d6df6e3-b7ea-4507-9d66-87c6ee8ed5b9", 461 | "timestamp": "2023-05-04T23:00:53Z", 462 | "quantity": 309, 463 | "unitPrice": 639.66, 464 | "totalAmount": 9964.61 465 | }, 466 | { 467 | "saleId": "201e23c6-6ddc-4dc9-8e39-b6fb81404fcf", 468 | "productId": "000a8c23-5bca-436c-a216-4e747a94c511", 469 | "timestamp": "2023-08-23T17:23:35Z", 470 | "quantity": 325, 471 | "unitPrice": 471.59, 472 | "totalAmount": 7706.41 473 | }, 474 | { 475 | "saleId": "603586d5-2f86-4c3e-9acf-cdffd433f702", 476 | "productId": "c5b600dc-6bfb-492a-b335-c3cc8c707959", 477 | "timestamp": "2024-02-22T06:16:55Z", 478 | "quantity": 857, 479 | "unitPrice": 197.36, 480 | "totalAmount": 760.65 481 | }, 482 | { 483 | "saleId": "5253cf24-44e6-4f86-8abe-d827a088ee9b", 484 | "productId": "9d5fafbc-312b-47e8-ada1-283918f0c3b5", 485 | "timestamp": "2023-12-15T21:21:09Z", 486 | "quantity": 25, 487 | "unitPrice": 158.12, 488 | "totalAmount": 243.15 489 | }, 490 | { 491 | "saleId": "31de695e-9c3d-433b-a5d5-3af401d2763e", 492 | "productId": "0114d5d4-ae48-46fa-b0ca-afe60eb88add", 493 | "timestamp": "2024-02-06T13:55:02Z", 494 | "quantity": 757, 495 | "unitPrice": 283.2, 496 | "totalAmount": 7420.27 497 | }, 498 | { 499 | "saleId": "beffcc21-4fa2-4abf-9ddf-03a68651f751", 500 | "productId": "e5b0da8c-148d-4680-b262-8609fb8a10da", 501 | "timestamp": "2023-05-20T14:26:19Z", 502 | "quantity": 580, 503 | "unitPrice": 659.75, 504 | "totalAmount": 5018.42 505 | }, 506 | { 507 | "saleId": "7c9389c3-87f0-454d-9ab3-f9c8fb28f082", 508 | "productId": "2be5b024-2c96-4f29-912c-c6f36353f799", 509 | "timestamp": "2023-07-01T04:46:47Z", 510 | "quantity": 948, 511 | "unitPrice": 620.57, 512 | "totalAmount": 7555.21 513 | }, 514 | { 515 | "saleId": "ada3165e-5ce6-45b9-95d5-4665b223f221", 516 | "productId": "fcf2e432-62a3-4b6f-a34d-36e42a12272e", 517 | "timestamp": "2023-03-25T13:26:56Z", 518 | "quantity": 960, 519 | "unitPrice": 989.43, 520 | "totalAmount": 1188.7 521 | }, 522 | { 523 | "saleId": "e8829f25-7ce4-44cd-bab9-277e1d2d170c", 524 | "productId": "fc4c81e5-f1ac-40f5-8c6f-da3fbad5599d", 525 | "timestamp": "2023-05-29T02:29:01Z", 526 | "quantity": 667, 527 | "unitPrice": 357.37, 528 | "totalAmount": 6196.09 529 | }, 530 | { 531 | "saleId": "984b0d72-f6ea-49ac-960f-f4027d2ea67b", 532 | "productId": "07238d8e-0037-4972-87ca-0df206ee3e42", 533 | "timestamp": "2023-03-15T22:27:44Z", 534 | "quantity": 623, 535 | "unitPrice": 467.77, 536 | "totalAmount": 6657.9 537 | }, 538 | { 539 | "saleId": "3027a79c-8583-4585-b618-a31344cb8acb", 540 | "productId": "154b7860-23a2-4564-ad99-1745ab7122ef", 541 | "timestamp": "2023-03-17T09:21:59Z", 542 | "quantity": 23, 543 | "unitPrice": 945.27, 544 | "totalAmount": 7870.76 545 | }, 546 | { 547 | "saleId": "90f35bc0-3617-4cb6-9754-923c814991fd", 548 | "productId": "8d4bf814-65d4-4df4-84cc-68911d925fdf", 549 | "timestamp": "2024-01-29T09:24:28Z", 550 | "quantity": 140, 551 | "unitPrice": 664.0, 552 | "totalAmount": 7582.02 553 | }, 554 | { 555 | "saleId": "5c86a794-9fac-4a1e-9c5a-01282a0b166e", 556 | "productId": "a52bf1bd-3d35-4cd2-849a-354e3952e2d2", 557 | "timestamp": "2023-05-25T21:06:21Z", 558 | "quantity": 127, 559 | "unitPrice": 185.81, 560 | "totalAmount": 4970.67 561 | }, 562 | { 563 | "saleId": "ec033faa-a469-48d5-b5e1-108413f12d07", 564 | "productId": "d35623ee-bef6-42b2-8776-2f99f8bb4782", 565 | "timestamp": "2023-12-12T16:58:27Z", 566 | "quantity": 178, 567 | "unitPrice": 422.07, 568 | "totalAmount": 169.06 569 | }, 570 | { 571 | "saleId": "0e8f8555-4af9-44a8-b32e-bc6d5d1b0de0", 572 | "productId": "8ac1ac77-7358-425e-be16-0bdde9f02e59", 573 | "timestamp": "2023-03-23T10:36:36Z", 574 | "quantity": 295, 575 | "unitPrice": 536.74, 576 | "totalAmount": 7652.54 577 | }, 578 | { 579 | "saleId": "2ac5fd37-ad3e-4a22-b37b-70603a30d82e", 580 | "productId": "1afc136b-4d9f-4e8e-aace-8e1df908a404", 581 | "timestamp": "2023-08-28T07:08:56Z", 582 | "quantity": 338, 583 | "unitPrice": 304.83, 584 | "totalAmount": 137.98 585 | }, 586 | { 587 | "saleId": "4ac03bce-3d3a-4d93-af99-9c7bc54e5168", 588 | "productId": "af84cc12-4fea-4f58-aece-f2ce92ca9580", 589 | "timestamp": "2024-02-15T09:35:47Z", 590 | "quantity": 65, 591 | "unitPrice": 951.61, 592 | "totalAmount": 7558.27 593 | }, 594 | { 595 | "saleId": "e354821f-9808-487c-b466-3d3a1c7bc762", 596 | "productId": "86e3bb1c-2f5d-4774-98f3-4df7cddd0a0f", 597 | "timestamp": "2023-11-25T20:30:36Z", 598 | "quantity": 171, 599 | "unitPrice": 305.65, 600 | "totalAmount": 4647.58 601 | }, 602 | { 603 | "saleId": "0ff9d397-db60-4281-8c51-73d6665003dd", 604 | "productId": "26b017c6-06d8-443f-9b4a-d6b1cee6f4c0", 605 | "timestamp": "2024-02-15T23:11:52Z", 606 | "quantity": 610, 607 | "unitPrice": 428.16, 608 | "totalAmount": 3294.23 609 | }, 610 | { 611 | "saleId": "1a5873ef-8484-40b0-a9fd-d6c11e441e61", 612 | "productId": "440c9e80-6bf8-4eb3-b2d2-f81936d67de3", 613 | "timestamp": "2024-03-10T20:18:24Z", 614 | "quantity": 579, 615 | "unitPrice": 129.19, 616 | "totalAmount": 488.19 617 | }, 618 | { 619 | "saleId": "d55ca8e1-ffff-439d-a55b-f0b9eb8f848e", 620 | "productId": "98255f4e-40a6-470f-89a5-0792729f8947", 621 | "timestamp": "2023-07-26T01:38:07Z", 622 | "quantity": 186, 623 | "unitPrice": 544.82, 624 | "totalAmount": 2989.5 625 | }, 626 | { 627 | "saleId": "e90068e8-55c5-400d-a805-7dbab388138a", 628 | "productId": "2a339fb2-f9f3-43bc-a85a-b217a0a38f12", 629 | "timestamp": "2023-09-24T10:51:03Z", 630 | "quantity": 540, 631 | "unitPrice": 541.07, 632 | "totalAmount": 3861.48 633 | }, 634 | { 635 | "saleId": "32eba028-2991-406a-90ca-600beb4f67db", 636 | "productId": "8a8391b2-b4ac-4847-b652-66ffd8d65875", 637 | "timestamp": "2023-06-29T10:24:41Z", 638 | "quantity": 661, 639 | "unitPrice": 78.07, 640 | "totalAmount": 2467.93 641 | }, 642 | { 643 | "saleId": "7c17b19e-17ef-4d37-9233-6ad31bd85b23", 644 | "productId": "be2157fb-7454-405e-9511-bf7ba81b7726", 645 | "timestamp": "2023-12-02T12:10:55Z", 646 | "quantity": 635, 647 | "unitPrice": 52.5, 648 | "totalAmount": 4393.68 649 | }, 650 | { 651 | "saleId": "729cfe78-6698-4a32-9c78-51ada37cfb36", 652 | "productId": "fdf1ba3d-fa06-4ce5-90ff-d081c5d37176", 653 | "timestamp": "2023-10-29T22:49:12Z", 654 | "quantity": 733, 655 | "unitPrice": 349.87, 656 | "totalAmount": 2736.72 657 | }, 658 | { 659 | "saleId": "48afeac9-385d-4ff3-9e7f-0a6787ec1958", 660 | "productId": "afded6df-058f-477d-9878-e0e0b1d3dff3", 661 | "timestamp": "2023-11-07T08:57:27Z", 662 | "quantity": 268, 663 | "unitPrice": 700.99, 664 | "totalAmount": 7115.25 665 | }, 666 | { 667 | "saleId": "0831a92f-4c18-4de6-a9d3-96ec3352603d", 668 | "productId": "daa29167-82a7-474b-9687-b8b903e7ec69", 669 | "timestamp": "2024-01-11T03:57:45Z", 670 | "quantity": 387, 671 | "unitPrice": 382.34, 672 | "totalAmount": 2575.24 673 | }, 674 | { 675 | "saleId": "2888cfa2-b9ef-4e6d-8c62-014734591691", 676 | "productId": "ccb83982-71f3-4497-bad8-7e64c6920dc6", 677 | "timestamp": "2023-11-23T13:32:43Z", 678 | "quantity": 420, 679 | "unitPrice": 596.87, 680 | "totalAmount": 3294.5 681 | }, 682 | { 683 | "saleId": "40bd92ec-084e-415e-ba4b-2d12d826ca36", 684 | "productId": "1936d406-e89e-40e4-bff7-1827532269d4", 685 | "timestamp": "2023-04-16T18:59:38Z", 686 | "quantity": 678, 687 | "unitPrice": 947.48, 688 | "totalAmount": 7363.69 689 | }, 690 | { 691 | "saleId": "2ba6fdbb-81c8-4af2-8adb-0505033a3c98", 692 | "productId": "c849a535-5f8b-47e3-889c-015693a644ac", 693 | "timestamp": "2024-01-27T13:21:55Z", 694 | "quantity": 626, 695 | "unitPrice": 731.66, 696 | "totalAmount": 9487.24 697 | }, 698 | { 699 | "saleId": "fc3006ad-f883-4f43-86ee-8c1a4f2c7248", 700 | "productId": "0c3e80ee-59b3-4fc4-b760-8b07acc2d3ae", 701 | "timestamp": "2023-11-02T09:32:22Z", 702 | "quantity": 732, 703 | "unitPrice": 366.09, 704 | "totalAmount": 4384.09 705 | }, 706 | { 707 | "saleId": "8b6c8765-001c-4a54-91e2-761394f1a700", 708 | "productId": "d8f5bee3-f3eb-4071-a124-6b857e0fd798", 709 | "timestamp": "2023-04-21T22:19:20Z", 710 | "quantity": 368, 711 | "unitPrice": 37.27, 712 | "totalAmount": 5510.41 713 | }, 714 | { 715 | "saleId": "b830c1e8-ed9a-4893-b6d2-68b87b991453", 716 | "productId": "8d15de86-0e4a-4414-9166-7a33610202d3", 717 | "timestamp": "2023-09-14T06:10:36Z", 718 | "quantity": 426, 719 | "unitPrice": 257.98, 720 | "totalAmount": 5241.93 721 | }, 722 | { 723 | "saleId": "eeaa3b86-09ae-46aa-9b58-6354713c23cd", 724 | "productId": "ea8fd0b9-c2d9-4d43-9c23-44cb99d079bb", 725 | "timestamp": "2023-05-09T05:06:12Z", 726 | "quantity": 228, 727 | "unitPrice": 205.53, 728 | "totalAmount": 2448.67 729 | }, 730 | { 731 | "saleId": "8579fa47-cd55-477e-9bc0-77f7975c91c6", 732 | "productId": "25d01c80-bca1-4a00-b1d0-0fbd39ff9e89", 733 | "timestamp": "2023-07-09T23:55:58Z", 734 | "quantity": 147, 735 | "unitPrice": 304.11, 736 | "totalAmount": 646.37 737 | }, 738 | { 739 | "saleId": "d5436830-dc0a-4f7b-b96b-ca96c10f7b36", 740 | "productId": "1d6df6e3-b7ea-4507-9d66-87c6ee8ed5b9", 741 | "timestamp": "2023-06-25T14:57:39Z", 742 | "quantity": 700, 743 | "unitPrice": 574.25, 744 | "totalAmount": 6103.38 745 | }, 746 | { 747 | "saleId": "477e5f5d-c44c-4f3a-b4c4-8a85924fe01b", 748 | "productId": "000a8c23-5bca-436c-a216-4e747a94c511", 749 | "timestamp": "2023-04-10T13:26:52Z", 750 | "quantity": 400, 751 | "unitPrice": 49.43, 752 | "totalAmount": 1860.5 753 | }, 754 | { 755 | "saleId": "bfbe67c7-3d3b-471c-a999-d2ea8a9594d0", 756 | "productId": "c5b600dc-6bfb-492a-b335-c3cc8c707959", 757 | "timestamp": "2023-11-17T17:02:08Z", 758 | "quantity": 654, 759 | "unitPrice": 825.63, 760 | "totalAmount": 6998.87 761 | }, 762 | { 763 | "saleId": "156a5c20-6338-41e2-b9ab-dbcb40905925", 764 | "productId": "9d5fafbc-312b-47e8-ada1-283918f0c3b5", 765 | "timestamp": "2023-03-20T16:53:47Z", 766 | "quantity": 125, 767 | "unitPrice": 237.24, 768 | "totalAmount": 6603.67 769 | }, 770 | { 771 | "saleId": "6d4ab0ae-5d49-4731-b099-d8e155fb49d1", 772 | "productId": "0114d5d4-ae48-46fa-b0ca-afe60eb88add", 773 | "timestamp": "2023-06-14T08:48:03Z", 774 | "quantity": 677, 775 | "unitPrice": 479.94, 776 | "totalAmount": 7760.82 777 | }, 778 | { 779 | "saleId": "81aa20f8-591f-47e6-8bad-7ad3e3003f81", 780 | "productId": "e5b0da8c-148d-4680-b262-8609fb8a10da", 781 | "timestamp": "2023-06-30T12:47:06Z", 782 | "quantity": 555, 783 | "unitPrice": 410.27, 784 | "totalAmount": 9330.17 785 | }, 786 | { 787 | "saleId": "a93e6a0c-d178-4889-b2ae-ba0325d9051f", 788 | "productId": "2be5b024-2c96-4f29-912c-c6f36353f799", 789 | "timestamp": "2023-11-24T12:14:40Z", 790 | "quantity": 508, 791 | "unitPrice": 558.25, 792 | "totalAmount": 4006.69 793 | }, 794 | { 795 | "saleId": "4a2575dd-8861-46ce-80ad-74f488d79a5a", 796 | "productId": "fcf2e432-62a3-4b6f-a34d-36e42a12272e", 797 | "timestamp": "2023-09-16T14:34:18Z", 798 | "quantity": 259, 799 | "unitPrice": 368.39, 800 | "totalAmount": 5611.94 801 | } 802 | ] 803 | -------------------------------------------------------------------------------- /server/prisma/seedData/purchases.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "purchaseId": "5035f91e-4a29-411a-8779-17f6105675f1", 4 | "productId": "d35623ee-bef6-42b2-8776-2f99f8bb4782", 5 | "timestamp": "2007-09-28T13:56:51Z", 6 | "quantity": 875, 7 | "unitCost": 4163.31, 8 | "totalCost": 7871.43 9 | }, 10 | { 11 | "purchaseId": "85841cb5-2132-40f2-b923-9769ee3c199b", 12 | "productId": "8ac1ac77-7358-425e-be16-0bdde9f02e59", 13 | "timestamp": "2015-02-01T13:04:43Z", 14 | "quantity": 471, 15 | "unitCost": 5673.17, 16 | "totalCost": 485.48 17 | }, 18 | { 19 | "purchaseId": "de384851-f898-4495-99b8-73448bb470bf", 20 | "productId": "1afc136b-4d9f-4e8e-aace-8e1df908a404", 21 | "timestamp": "2010-10-22T19:38:20Z", 22 | "quantity": 37, 23 | "unitCost": 3835.06, 24 | "totalCost": 4202.25 25 | }, 26 | { 27 | "purchaseId": "e0b0486c-6396-42b7-9a84-e21bd3a88600", 28 | "productId": "af84cc12-4fea-4f58-aece-f2ce92ca9580", 29 | "timestamp": "2020-01-25T13:30:58Z", 30 | "quantity": 201, 31 | "unitCost": 1822.27, 32 | "totalCost": 8028.29 33 | }, 34 | { 35 | "purchaseId": "822e6025-e582-4e8c-b143-dc5ebad4c18c", 36 | "productId": "86e3bb1c-2f5d-4774-98f3-4df7cddd0a0f", 37 | "timestamp": "2011-12-30T12:54:41Z", 38 | "quantity": 789, 39 | "unitCost": 9238.02, 40 | "totalCost": 5086.57 41 | }, 42 | { 43 | "purchaseId": "dd6d3a14-92d5-40f4-9bf4-92fb4043882c", 44 | "productId": "26b017c6-06d8-443f-9b4a-d6b1cee6f4c0", 45 | "timestamp": "2014-02-26T01:13:31Z", 46 | "quantity": 251, 47 | "unitCost": 2443.32, 48 | "totalCost": 9520.42 49 | }, 50 | { 51 | "purchaseId": "434e5415-cb23-4170-b1e7-bc85737dc8c7", 52 | "productId": "440c9e80-6bf8-4eb3-b2d2-f81936d67de3", 53 | "timestamp": "2001-11-18T23:49:33Z", 54 | "quantity": 418, 55 | "unitCost": 5085.79, 56 | "totalCost": 5394.89 57 | }, 58 | { 59 | "purchaseId": "963a92ab-e092-4f3d-8b6a-5ab803550d78", 60 | "productId": "98255f4e-40a6-470f-89a5-0792729f8947", 61 | "timestamp": "2020-04-27T08:53:44Z", 62 | "quantity": 926, 63 | "unitCost": 8626.52, 64 | "totalCost": 3526.35 65 | }, 66 | { 67 | "purchaseId": "b4aebfdc-bce9-4e90-89fc-5098447d687a", 68 | "productId": "2a339fb2-f9f3-43bc-a85a-b217a0a38f12", 69 | "timestamp": "2020-02-05T13:02:55Z", 70 | "quantity": 754, 71 | "unitCost": 9052.79, 72 | "totalCost": 8035.97 73 | }, 74 | { 75 | "purchaseId": "de2ba6a2-c291-4a60-aad1-23d5749c5847", 76 | "productId": "8a8391b2-b4ac-4847-b652-66ffd8d65875", 77 | "timestamp": "2020-05-07T16:41:13Z", 78 | "quantity": 365, 79 | "unitCost": 1105.02, 80 | "totalCost": 361.77 81 | }, 82 | { 83 | "purchaseId": "78ea5f20-8fcc-4b6c-836f-e4b7a58d7479", 84 | "productId": "be2157fb-7454-405e-9511-bf7ba81b7726", 85 | "timestamp": "2014-08-12T16:37:41Z", 86 | "quantity": 975, 87 | "unitCost": 4783.47, 88 | "totalCost": 9517.85 89 | }, 90 | { 91 | "purchaseId": "61ba57e9-81fe-4d04-85b5-6aab219ced9d", 92 | "productId": "fdf1ba3d-fa06-4ce5-90ff-d081c5d37176", 93 | "timestamp": "2008-12-07T16:18:59Z", 94 | "quantity": 286, 95 | "unitCost": 6910.82, 96 | "totalCost": 9006.07 97 | }, 98 | { 99 | "purchaseId": "012d028b-4d6b-43bf-8d69-edf296133c9e", 100 | "productId": "afded6df-058f-477d-9878-e0e0b1d3dff3", 101 | "timestamp": "2005-12-29T10:50:55Z", 102 | "quantity": 71, 103 | "unitCost": 4058.81, 104 | "totalCost": 5983.42 105 | }, 106 | { 107 | "purchaseId": "8155b76c-3da2-4fb4-ba18-68bf8a2cec1b", 108 | "productId": "daa29167-82a7-474b-9687-b8b903e7ec69", 109 | "timestamp": "2007-08-15T18:41:49Z", 110 | "quantity": 963, 111 | "unitCost": 2198.44, 112 | "totalCost": 4935.68 113 | }, 114 | { 115 | "purchaseId": "4d449c76-ee8a-473e-8135-f74875a31964", 116 | "productId": "ccb83982-71f3-4497-bad8-7e64c6920dc6", 117 | "timestamp": "2014-12-05T06:28:48Z", 118 | "quantity": 614, 119 | "unitCost": 6565.99, 120 | "totalCost": 6406.98 121 | }, 122 | { 123 | "purchaseId": "115c6e6c-e6d0-418f-990e-1730f9e4b02e", 124 | "productId": "1936d406-e89e-40e4-bff7-1827532269d4", 125 | "timestamp": "2003-12-17T12:52:02Z", 126 | "quantity": 193, 127 | "unitCost": 2562.72, 128 | "totalCost": 788.08 129 | }, 130 | { 131 | "purchaseId": "7b898dfe-79e0-472c-a19e-d75d6a2719fd", 132 | "productId": "c849a535-5f8b-47e3-889c-015693a644ac", 133 | "timestamp": "2014-05-13T17:09:07Z", 134 | "quantity": 585, 135 | "unitCost": 1580.69, 136 | "totalCost": 2633.43 137 | }, 138 | { 139 | "purchaseId": "cb3000e8-df2e-4e8b-99ed-4ceb35077404", 140 | "productId": "0c3e80ee-59b3-4fc4-b760-8b07acc2d3ae", 141 | "timestamp": "2006-10-21T15:28:13Z", 142 | "quantity": 938, 143 | "unitCost": 6197.05, 144 | "totalCost": 3487.61 145 | }, 146 | { 147 | "purchaseId": "8b709837-e51b-493a-8720-89e64f9888e2", 148 | "productId": "d8f5bee3-f3eb-4071-a124-6b857e0fd798", 149 | "timestamp": "2002-08-23T21:20:02Z", 150 | "quantity": 388, 151 | "unitCost": 3280.29, 152 | "totalCost": 21.24 153 | }, 154 | { 155 | "purchaseId": "8ee49368-1e96-41ed-bc53-7ba90ed537a4", 156 | "productId": "8d15de86-0e4a-4414-9166-7a33610202d3", 157 | "timestamp": "2022-09-09T02:34:59Z", 158 | "quantity": 463, 159 | "unitCost": 4646.46, 160 | "totalCost": 273.21 161 | }, 162 | { 163 | "purchaseId": "a6d93761-366f-4e66-9a2f-2621d94896cb", 164 | "productId": "ea8fd0b9-c2d9-4d43-9c23-44cb99d079bb", 165 | "timestamp": "2013-04-06T06:29:40Z", 166 | "quantity": 893, 167 | "unitCost": 2857.95, 168 | "totalCost": 7423.21 169 | }, 170 | { 171 | "purchaseId": "c6207ac3-3c64-49f4-9383-e47c32f11c28", 172 | "productId": "25d01c80-bca1-4a00-b1d0-0fbd39ff9e89", 173 | "timestamp": "2002-06-17T12:03:13Z", 174 | "quantity": 22, 175 | "unitCost": 3180.14, 176 | "totalCost": 9077.46 177 | }, 178 | { 179 | "purchaseId": "02cb1517-154a-469e-88a1-33449be5b6de", 180 | "productId": "1d6df6e3-b7ea-4507-9d66-87c6ee8ed5b9", 181 | "timestamp": "2017-05-26T03:42:34Z", 182 | "quantity": 70, 183 | "unitCost": 4308.81, 184 | "totalCost": 1589.27 185 | }, 186 | { 187 | "purchaseId": "6e468dfb-8cfc-4ad6-af0c-af0a1f6726ab", 188 | "productId": "000a8c23-5bca-436c-a216-4e747a94c511", 189 | "timestamp": "2013-02-03T13:03:56Z", 190 | "quantity": 208, 191 | "unitCost": 3424.81, 192 | "totalCost": 9545.07 193 | }, 194 | { 195 | "purchaseId": "0f67af62-d82b-4986-ba91-0d7cc8261e21", 196 | "productId": "c5b600dc-6bfb-492a-b335-c3cc8c707959", 197 | "timestamp": "2020-03-16T04:44:49Z", 198 | "quantity": 309, 199 | "unitCost": 9757.04, 200 | "totalCost": 9843.24 201 | }, 202 | { 203 | "purchaseId": "c7539c9a-06a1-4e1c-aba1-4b7c60354974", 204 | "productId": "9d5fafbc-312b-47e8-ada1-283918f0c3b5", 205 | "timestamp": "2011-08-01T04:44:36Z", 206 | "quantity": 97, 207 | "unitCost": 4437.3, 208 | "totalCost": 7227.92 209 | }, 210 | { 211 | "purchaseId": "80e5fcca-73a3-4679-b27f-21328a77e077", 212 | "productId": "0114d5d4-ae48-46fa-b0ca-afe60eb88add", 213 | "timestamp": "2021-10-23T09:58:14Z", 214 | "quantity": 727, 215 | "unitCost": 6706.78, 216 | "totalCost": 3444.68 217 | }, 218 | { 219 | "purchaseId": "33d99aae-0b34-4c3c-af34-dba1201fa025", 220 | "productId": "e5b0da8c-148d-4680-b262-8609fb8a10da", 221 | "timestamp": "2013-12-25T00:37:26Z", 222 | "quantity": 246, 223 | "unitCost": 7074.43, 224 | "totalCost": 1551.41 225 | }, 226 | { 227 | "purchaseId": "0a05c7b9-01a2-48d2-b1ed-51e11dc50550", 228 | "productId": "2be5b024-2c96-4f29-912c-c6f36353f799", 229 | "timestamp": "2001-10-29T19:34:59Z", 230 | "quantity": 193, 231 | "unitCost": 1576.29, 232 | "totalCost": 5458.34 233 | }, 234 | { 235 | "purchaseId": "4bbd5b44-51ea-40cb-b993-8c1c1f3d6146", 236 | "productId": "fcf2e432-62a3-4b6f-a34d-36e42a12272e", 237 | "timestamp": "2008-05-09T00:34:59Z", 238 | "quantity": 265, 239 | "unitCost": 9629.95, 240 | "totalCost": 9902.55 241 | }, 242 | { 243 | "purchaseId": "1dc72d06-61c1-4606-b604-15fcd1a3943e", 244 | "productId": "fc4c81e5-f1ac-40f5-8c6f-da3fbad5599d", 245 | "timestamp": "2018-12-12T10:12:24Z", 246 | "quantity": 342, 247 | "unitCost": 9107.59, 248 | "totalCost": 9187.51 249 | }, 250 | { 251 | "purchaseId": "51f66b06-f965-433c-9fca-35e344324b3f", 252 | "productId": "07238d8e-0037-4972-87ca-0df206ee3e42", 253 | "timestamp": "2022-11-23T16:45:20Z", 254 | "quantity": 623, 255 | "unitCost": 3511.85, 256 | "totalCost": 8448.84 257 | }, 258 | { 259 | "purchaseId": "0669c359-14ad-488f-bf66-553cb4263a06", 260 | "productId": "154b7860-23a2-4564-ad99-1745ab7122ef", 261 | "timestamp": "2016-06-18T10:26:07Z", 262 | "quantity": 856, 263 | "unitCost": 584.88, 264 | "totalCost": 6455.21 265 | }, 266 | { 267 | "purchaseId": "5e697ec5-7722-48b5-b682-c3752a11b62e", 268 | "productId": "8d4bf814-65d4-4df4-84cc-68911d925fdf", 269 | "timestamp": "2003-08-07T17:41:10Z", 270 | "quantity": 330, 271 | "unitCost": 9835.78, 272 | "totalCost": 311.95 273 | }, 274 | { 275 | "purchaseId": "3e54b486-1044-46b6-82f9-2646d92d4afa", 276 | "productId": "a52bf1bd-3d35-4cd2-849a-354e3952e2d2", 277 | "timestamp": "2007-02-01T19:40:46Z", 278 | "quantity": 265, 279 | "unitCost": 6506.44, 280 | "totalCost": 6583.9 281 | }, 282 | { 283 | "purchaseId": "3726ca7b-1f32-4cad-a21b-6112de7d73ad", 284 | "productId": "d35623ee-bef6-42b2-8776-2f99f8bb4782", 285 | "timestamp": "2012-07-16T01:03:33Z", 286 | "quantity": 395, 287 | "unitCost": 4122.4, 288 | "totalCost": 4838.66 289 | }, 290 | { 291 | "purchaseId": "54e4ebc5-b083-42f3-a3ec-90dcd64923f3", 292 | "productId": "8ac1ac77-7358-425e-be16-0bdde9f02e59", 293 | "timestamp": "2002-02-19T15:33:07Z", 294 | "quantity": 230, 295 | "unitCost": 2500.44, 296 | "totalCost": 7003.48 297 | }, 298 | { 299 | "purchaseId": "21af41db-67ee-42fb-ad12-8e53c9017a21", 300 | "productId": "1afc136b-4d9f-4e8e-aace-8e1df908a404", 301 | "timestamp": "2005-04-20T20:56:35Z", 302 | "quantity": 770, 303 | "unitCost": 3563.47, 304 | "totalCost": 2068.71 305 | }, 306 | { 307 | "purchaseId": "6efcb79a-8db6-4271-b28b-cfb46441d6d8", 308 | "productId": "af84cc12-4fea-4f58-aece-f2ce92ca9580", 309 | "timestamp": "2013-01-08T17:38:44Z", 310 | "quantity": 903, 311 | "unitCost": 6854.34, 312 | "totalCost": 6894.18 313 | }, 314 | { 315 | "purchaseId": "a5d27d65-6419-4741-94f0-9499151f9031", 316 | "productId": "86e3bb1c-2f5d-4774-98f3-4df7cddd0a0f", 317 | "timestamp": "2018-11-16T06:44:31Z", 318 | "quantity": 220, 319 | "unitCost": 170.61, 320 | "totalCost": 3140.28 321 | }, 322 | { 323 | "purchaseId": "0ffb160a-c687-4155-bea3-fc3ecc7da237", 324 | "productId": "26b017c6-06d8-443f-9b4a-d6b1cee6f4c0", 325 | "timestamp": "2022-02-13T05:52:33Z", 326 | "quantity": 625, 327 | "unitCost": 5823.23, 328 | "totalCost": 4498.82 329 | }, 330 | { 331 | "purchaseId": "f96e84fc-3b9c-4f96-b80c-0646752c2624", 332 | "productId": "440c9e80-6bf8-4eb3-b2d2-f81936d67de3", 333 | "timestamp": "2008-05-05T23:57:50Z", 334 | "quantity": 260, 335 | "unitCost": 1320.91, 336 | "totalCost": 6951.15 337 | }, 338 | { 339 | "purchaseId": "65419b2f-d156-408b-bfb6-52846e3aca1d", 340 | "productId": "98255f4e-40a6-470f-89a5-0792729f8947", 341 | "timestamp": "2010-06-27T23:54:15Z", 342 | "quantity": 72, 343 | "unitCost": 6999.3, 344 | "totalCost": 4171.88 345 | }, 346 | { 347 | "purchaseId": "cd59eb23-5b53-4a0c-a1a0-0950c5ea17f4", 348 | "productId": "2a339fb2-f9f3-43bc-a85a-b217a0a38f12", 349 | "timestamp": "2018-06-04T02:16:54Z", 350 | "quantity": 846, 351 | "unitCost": 1424.7, 352 | "totalCost": 604.87 353 | }, 354 | { 355 | "purchaseId": "31b464d9-89a7-431e-9bf1-9d68ee05d549", 356 | "productId": "8a8391b2-b4ac-4847-b652-66ffd8d65875", 357 | "timestamp": "2019-12-08T22:05:22Z", 358 | "quantity": 998, 359 | "unitCost": 9289.05, 360 | "totalCost": 3108.03 361 | }, 362 | { 363 | "purchaseId": "767fc135-43ca-4067-87bb-0ab503bbd4c3", 364 | "productId": "be2157fb-7454-405e-9511-bf7ba81b7726", 365 | "timestamp": "2005-01-25T15:21:10Z", 366 | "quantity": 311, 367 | "unitCost": 1386.68, 368 | "totalCost": 8924.26 369 | }, 370 | { 371 | "purchaseId": "52ce8633-b4cc-455d-94ab-531a7fb5b720", 372 | "productId": "fdf1ba3d-fa06-4ce5-90ff-d081c5d37176", 373 | "timestamp": "2022-09-27T13:23:04Z", 374 | "quantity": 71, 375 | "unitCost": 6751.76, 376 | "totalCost": 2580.36 377 | }, 378 | { 379 | "purchaseId": "e292f886-0f8d-4989-bc01-f43dfffde54e", 380 | "productId": "afded6df-058f-477d-9878-e0e0b1d3dff3", 381 | "timestamp": "2008-10-13T00:50:04Z", 382 | "quantity": 134, 383 | "unitCost": 4446.17, 384 | "totalCost": 1718.57 385 | }, 386 | { 387 | "purchaseId": "42e2086e-6dda-4c8e-b635-4387d794d6a0", 388 | "productId": "daa29167-82a7-474b-9687-b8b903e7ec69", 389 | "timestamp": "2014-09-16T22:59:06Z", 390 | "quantity": 807, 391 | "unitCost": 9916.73, 392 | "totalCost": 6818.54 393 | }, 394 | { 395 | "purchaseId": "c223c7fe-38d5-4291-b618-c44bd9f797a7", 396 | "productId": "ccb83982-71f3-4497-bad8-7e64c6920dc6", 397 | "timestamp": "2007-03-28T02:21:31Z", 398 | "quantity": 726, 399 | "unitCost": 2967.87, 400 | "totalCost": 5509.38 401 | }, 402 | { 403 | "purchaseId": "e12f6fd6-af77-4556-8866-c724d88acc5d", 404 | "productId": "1936d406-e89e-40e4-bff7-1827532269d4", 405 | "timestamp": "2010-09-20T14:09:37Z", 406 | "quantity": 763, 407 | "unitCost": 2083.13, 408 | "totalCost": 9062.53 409 | }, 410 | { 411 | "purchaseId": "503fbb6e-2c84-4293-ad1e-8ad1a4ea3c8f", 412 | "productId": "c849a535-5f8b-47e3-889c-015693a644ac", 413 | "timestamp": "2016-08-03T19:45:31Z", 414 | "quantity": 675, 415 | "unitCost": 6611.91, 416 | "totalCost": 2353.38 417 | }, 418 | { 419 | "purchaseId": "418785b8-d434-4f1b-827b-52584eb91414", 420 | "productId": "0c3e80ee-59b3-4fc4-b760-8b07acc2d3ae", 421 | "timestamp": "2005-03-28T16:39:46Z", 422 | "quantity": 839, 423 | "unitCost": 1488.73, 424 | "totalCost": 403.2 425 | }, 426 | { 427 | "purchaseId": "032f3bc6-1edf-45ee-8cdd-7a3dbd4ec6bd", 428 | "productId": "d8f5bee3-f3eb-4071-a124-6b857e0fd798", 429 | "timestamp": "2010-09-11T18:58:14Z", 430 | "quantity": 776, 431 | "unitCost": 9893.08, 432 | "totalCost": 2071.46 433 | }, 434 | { 435 | "purchaseId": "f64f8bfd-7c71-4bb0-a74e-21907a8b2a71", 436 | "productId": "8d15de86-0e4a-4414-9166-7a33610202d3", 437 | "timestamp": "2011-06-19T11:19:53Z", 438 | "quantity": 720, 439 | "unitCost": 3132.2, 440 | "totalCost": 1104.45 441 | }, 442 | { 443 | "purchaseId": "aa148601-a438-4c9a-ba20-6645945e750b", 444 | "productId": "ea8fd0b9-c2d9-4d43-9c23-44cb99d079bb", 445 | "timestamp": "2001-12-13T15:26:09Z", 446 | "quantity": 392, 447 | "unitCost": 4880.07, 448 | "totalCost": 7915.32 449 | }, 450 | { 451 | "purchaseId": "feabf3d1-9727-4b03-bd5c-2c97d85fc91e", 452 | "productId": "25d01c80-bca1-4a00-b1d0-0fbd39ff9e89", 453 | "timestamp": "2019-07-06T09:30:09Z", 454 | "quantity": 265, 455 | "unitCost": 2609.83, 456 | "totalCost": 1980.18 457 | }, 458 | { 459 | "purchaseId": "6f44cf51-39cf-46c6-8e7e-719347ec2124", 460 | "productId": "1d6df6e3-b7ea-4507-9d66-87c6ee8ed5b9", 461 | "timestamp": "2005-08-17T21:00:54Z", 462 | "quantity": 984, 463 | "unitCost": 8085.15, 464 | "totalCost": 8511.07 465 | }, 466 | { 467 | "purchaseId": "e76ebddb-90a1-4916-a3bd-17b5c58855dd", 468 | "productId": "000a8c23-5bca-436c-a216-4e747a94c511", 469 | "timestamp": "2003-10-14T08:56:13Z", 470 | "quantity": 575, 471 | "unitCost": 6609.95, 472 | "totalCost": 3212.6 473 | }, 474 | { 475 | "purchaseId": "00fb05b1-724d-4b82-8600-441d20127ecb", 476 | "productId": "c5b600dc-6bfb-492a-b335-c3cc8c707959", 477 | "timestamp": "2009-01-26T02:37:46Z", 478 | "quantity": 324, 479 | "unitCost": 5032.83, 480 | "totalCost": 6453.79 481 | }, 482 | { 483 | "purchaseId": "2b5d452d-be4f-4fc0-a018-a156b0aa144a", 484 | "productId": "9d5fafbc-312b-47e8-ada1-283918f0c3b5", 485 | "timestamp": "2017-09-20T01:05:24Z", 486 | "quantity": 859, 487 | "unitCost": 9826.12, 488 | "totalCost": 4717.49 489 | }, 490 | { 491 | "purchaseId": "8272d8dc-df85-455c-b2ec-7cc4611a570c", 492 | "productId": "0114d5d4-ae48-46fa-b0ca-afe60eb88add", 493 | "timestamp": "2014-11-05T03:06:06Z", 494 | "quantity": 713, 495 | "unitCost": 8238.67, 496 | "totalCost": 6012.29 497 | }, 498 | { 499 | "purchaseId": "5d8b58d2-63b1-4419-ab46-16ed3664adfc", 500 | "productId": "e5b0da8c-148d-4680-b262-8609fb8a10da", 501 | "timestamp": "2008-11-29T17:59:44Z", 502 | "quantity": 230, 503 | "unitCost": 684.84, 504 | "totalCost": 202.1 505 | }, 506 | { 507 | "purchaseId": "e33fabca-19cf-469b-b3e7-6e5f1f434a3e", 508 | "productId": "2be5b024-2c96-4f29-912c-c6f36353f799", 509 | "timestamp": "2018-03-14T04:27:34Z", 510 | "quantity": 247, 511 | "unitCost": 7781.32, 512 | "totalCost": 55.1 513 | }, 514 | { 515 | "purchaseId": "17c90a13-0871-44a5-9dcf-9b46d756b780", 516 | "productId": "fcf2e432-62a3-4b6f-a34d-36e42a12272e", 517 | "timestamp": "2010-05-03T03:18:22Z", 518 | "quantity": 267, 519 | "unitCost": 5.18, 520 | "totalCost": 5559.82 521 | }, 522 | { 523 | "purchaseId": "22be5e72-440c-4a02-8176-9eb50d9cdc99", 524 | "productId": "fc4c81e5-f1ac-40f5-8c6f-da3fbad5599d", 525 | "timestamp": "2001-09-14T20:35:26Z", 526 | "quantity": 298, 527 | "unitCost": 388.02, 528 | "totalCost": 364.51 529 | }, 530 | { 531 | "purchaseId": "3b663fc6-e550-4513-9df4-ecd7399e3ad4", 532 | "productId": "07238d8e-0037-4972-87ca-0df206ee3e42", 533 | "timestamp": "2021-07-11T20:29:31Z", 534 | "quantity": 356, 535 | "unitCost": 3099.1, 536 | "totalCost": 9858.3 537 | }, 538 | { 539 | "purchaseId": "a7cd1009-cca3-4dd0-a08e-519fbef15886", 540 | "productId": "154b7860-23a2-4564-ad99-1745ab7122ef", 541 | "timestamp": "2015-11-29T04:47:23Z", 542 | "quantity": 560, 543 | "unitCost": 8579.08, 544 | "totalCost": 6600.09 545 | }, 546 | { 547 | "purchaseId": "08232378-99cf-4828-b36f-6d7a31baba64", 548 | "productId": "8d4bf814-65d4-4df4-84cc-68911d925fdf", 549 | "timestamp": "2016-09-15T07:58:08Z", 550 | "quantity": 843, 551 | "unitCost": 556.0, 552 | "totalCost": 6556.65 553 | }, 554 | { 555 | "purchaseId": "d33fe221-fd05-4f75-a8cd-8540ab8e8ce6", 556 | "productId": "a52bf1bd-3d35-4cd2-849a-354e3952e2d2", 557 | "timestamp": "2016-03-27T16:52:42Z", 558 | "quantity": 288, 559 | "unitCost": 9563.81, 560 | "totalCost": 1649.02 561 | }, 562 | { 563 | "purchaseId": "9cd56445-6055-450a-a992-e3ae14bb3338", 564 | "productId": "d35623ee-bef6-42b2-8776-2f99f8bb4782", 565 | "timestamp": "2000-03-20T02:42:32Z", 566 | "quantity": 725, 567 | "unitCost": 9495.08, 568 | "totalCost": 5749.92 569 | }, 570 | { 571 | "purchaseId": "fd6d7c0a-5d02-4dbc-8217-1d826eb569ce", 572 | "productId": "8ac1ac77-7358-425e-be16-0bdde9f02e59", 573 | "timestamp": "2012-09-14T23:33:07Z", 574 | "quantity": 141, 575 | "unitCost": 4504.95, 576 | "totalCost": 1561.89 577 | }, 578 | { 579 | "purchaseId": "fe28b71d-8883-4157-ac89-4a5105e8ff54", 580 | "productId": "1afc136b-4d9f-4e8e-aace-8e1df908a404", 581 | "timestamp": "2008-01-02T02:33:03Z", 582 | "quantity": 388, 583 | "unitCost": 6425.96, 584 | "totalCost": 837.03 585 | }, 586 | { 587 | "purchaseId": "93a95d90-2d95-4e21-9d61-deb872b8ce23", 588 | "productId": "af84cc12-4fea-4f58-aece-f2ce92ca9580", 589 | "timestamp": "2018-06-18T17:24:06Z", 590 | "quantity": 194, 591 | "unitCost": 8925.08, 592 | "totalCost": 8644.66 593 | }, 594 | { 595 | "purchaseId": "52a78535-b622-4c9e-834c-21ca705d07b5", 596 | "productId": "86e3bb1c-2f5d-4774-98f3-4df7cddd0a0f", 597 | "timestamp": "2022-11-06T03:27:35Z", 598 | "quantity": 873, 599 | "unitCost": 7537.82, 600 | "totalCost": 1841.36 601 | }, 602 | { 603 | "purchaseId": "ed4adaea-bcd2-49e5-bc9e-84ed807fa04c", 604 | "productId": "26b017c6-06d8-443f-9b4a-d6b1cee6f4c0", 605 | "timestamp": "2022-04-20T14:20:15Z", 606 | "quantity": 70, 607 | "unitCost": 5186.13, 608 | "totalCost": 9731.04 609 | }, 610 | { 611 | "purchaseId": "1ee44e30-7a8f-4e88-b6dd-01c11b26f6b9", 612 | "productId": "440c9e80-6bf8-4eb3-b2d2-f81936d67de3", 613 | "timestamp": "2008-04-18T13:02:07Z", 614 | "quantity": 10, 615 | "unitCost": 6835.47, 616 | "totalCost": 4138.19 617 | }, 618 | { 619 | "purchaseId": "913bc6d9-6757-457c-a565-471396e4f5b5", 620 | "productId": "98255f4e-40a6-470f-89a5-0792729f8947", 621 | "timestamp": "2010-06-26T01:51:22Z", 622 | "quantity": 651, 623 | "unitCost": 1095.15, 624 | "totalCost": 503.43 625 | }, 626 | { 627 | "purchaseId": "d33baec5-186c-428d-996d-6dbd12e20c10", 628 | "productId": "2a339fb2-f9f3-43bc-a85a-b217a0a38f12", 629 | "timestamp": "2015-06-01T00:52:08Z", 630 | "quantity": 638, 631 | "unitCost": 2282.03, 632 | "totalCost": 5722.0 633 | }, 634 | { 635 | "purchaseId": "6eb61d25-4cf6-43f8-a52d-302cf0b29553", 636 | "productId": "8a8391b2-b4ac-4847-b652-66ffd8d65875", 637 | "timestamp": "2011-04-19T21:39:04Z", 638 | "quantity": 67, 639 | "unitCost": 5414.05, 640 | "totalCost": 1905.77 641 | }, 642 | { 643 | "purchaseId": "37b1920b-5a4b-41c7-bbf0-a872e93f2ab3", 644 | "productId": "be2157fb-7454-405e-9511-bf7ba81b7726", 645 | "timestamp": "2008-12-12T12:16:22Z", 646 | "quantity": 775, 647 | "unitCost": 333.25, 648 | "totalCost": 2883.66 649 | }, 650 | { 651 | "purchaseId": "9b4f68c8-2d1a-44c8-95e9-3b257a6c37e3", 652 | "productId": "fdf1ba3d-fa06-4ce5-90ff-d081c5d37176", 653 | "timestamp": "2020-12-29T16:46:08Z", 654 | "quantity": 513, 655 | "unitCost": 6781.28, 656 | "totalCost": 5454.83 657 | }, 658 | { 659 | "purchaseId": "87c3172d-9ae8-4163-b8ef-ad638fa27c95", 660 | "productId": "afded6df-058f-477d-9878-e0e0b1d3dff3", 661 | "timestamp": "2019-04-05T05:20:27Z", 662 | "quantity": 13, 663 | "unitCost": 5414.02, 664 | "totalCost": 4540.62 665 | }, 666 | { 667 | "purchaseId": "8aed5186-cb4a-4bbb-b8be-857ff8227289", 668 | "productId": "daa29167-82a7-474b-9687-b8b903e7ec69", 669 | "timestamp": "2019-05-19T23:47:06Z", 670 | "quantity": 522, 671 | "unitCost": 2047.88, 672 | "totalCost": 456.65 673 | }, 674 | { 675 | "purchaseId": "a5e1bed8-ced3-41cd-af3b-d891e01e1c1d", 676 | "productId": "ccb83982-71f3-4497-bad8-7e64c6920dc6", 677 | "timestamp": "2018-12-19T05:41:28Z", 678 | "quantity": 20, 679 | "unitCost": 7741.5, 680 | "totalCost": 6205.14 681 | }, 682 | { 683 | "purchaseId": "dcc30321-e243-4cce-808a-2e0c553eee94", 684 | "productId": "1936d406-e89e-40e4-bff7-1827532269d4", 685 | "timestamp": "2002-10-06T13:58:33Z", 686 | "quantity": 473, 687 | "unitCost": 8571.71, 688 | "totalCost": 3088.43 689 | }, 690 | { 691 | "purchaseId": "d3462347-2e06-46ff-9088-ceb25ee3ed11", 692 | "productId": "c849a535-5f8b-47e3-889c-015693a644ac", 693 | "timestamp": "2008-10-07T18:13:43Z", 694 | "quantity": 621, 695 | "unitCost": 2592.95, 696 | "totalCost": 5944.58 697 | }, 698 | { 699 | "purchaseId": "24bb1ac4-4d5c-4308-a6f9-a004bd819f5e", 700 | "productId": "0c3e80ee-59b3-4fc4-b760-8b07acc2d3ae", 701 | "timestamp": "2003-08-29T16:23:44Z", 702 | "quantity": 240, 703 | "unitCost": 7732.65, 704 | "totalCost": 4956.83 705 | }, 706 | { 707 | "purchaseId": "42d5ade2-6b62-47f0-aaa4-0b065a0b73ff", 708 | "productId": "d8f5bee3-f3eb-4071-a124-6b857e0fd798", 709 | "timestamp": "2020-08-16T06:05:41Z", 710 | "quantity": 729, 711 | "unitCost": 9648.57, 712 | "totalCost": 9558.36 713 | }, 714 | { 715 | "purchaseId": "394dd7bb-d9ca-4ded-87ca-6336ca68f06c", 716 | "productId": "8d15de86-0e4a-4414-9166-7a33610202d3", 717 | "timestamp": "2019-06-19T07:23:51Z", 718 | "quantity": 109, 719 | "unitCost": 6608.23, 720 | "totalCost": 8900.4 721 | }, 722 | { 723 | "purchaseId": "312e3902-2aaa-4678-bafb-534ec5fc1c24", 724 | "productId": "ea8fd0b9-c2d9-4d43-9c23-44cb99d079bb", 725 | "timestamp": "2014-03-12T17:47:55Z", 726 | "quantity": 490, 727 | "unitCost": 4139.51, 728 | "totalCost": 428.37 729 | }, 730 | { 731 | "purchaseId": "9685898d-0b3c-4500-98cf-101e4e5818cf", 732 | "productId": "25d01c80-bca1-4a00-b1d0-0fbd39ff9e89", 733 | "timestamp": "2002-12-27T20:19:43Z", 734 | "quantity": 926, 735 | "unitCost": 305.23, 736 | "totalCost": 326.1 737 | }, 738 | { 739 | "purchaseId": "c67d1950-3c03-401e-9f4a-573cc919c3ff", 740 | "productId": "1d6df6e3-b7ea-4507-9d66-87c6ee8ed5b9", 741 | "timestamp": "2000-03-28T15:44:26Z", 742 | "quantity": 210, 743 | "unitCost": 838.57, 744 | "totalCost": 3004.31 745 | }, 746 | { 747 | "purchaseId": "0d793046-8eff-412c-983e-c13196c331cf", 748 | "productId": "000a8c23-5bca-436c-a216-4e747a94c511", 749 | "timestamp": "2009-09-30T02:48:52Z", 750 | "quantity": 452, 751 | "unitCost": 6538.73, 752 | "totalCost": 9188.61 753 | }, 754 | { 755 | "purchaseId": "b38af82a-d3fb-46ec-9f98-346faf95629f", 756 | "productId": "c5b600dc-6bfb-492a-b335-c3cc8c707959", 757 | "timestamp": "2018-01-17T10:45:44Z", 758 | "quantity": 418, 759 | "unitCost": 7846.58, 760 | "totalCost": 9258.71 761 | }, 762 | { 763 | "purchaseId": "32e25004-21cf-44ab-b44e-e3580d0ef9ce", 764 | "productId": "9d5fafbc-312b-47e8-ada1-283918f0c3b5", 765 | "timestamp": "2004-07-18T14:25:34Z", 766 | "quantity": 902, 767 | "unitCost": 1056.47, 768 | "totalCost": 4319.07 769 | }, 770 | { 771 | "purchaseId": "0bfea984-8cdd-45ee-9407-5c9da64f94b0", 772 | "productId": "0114d5d4-ae48-46fa-b0ca-afe60eb88add", 773 | "timestamp": "2017-01-24T20:41:03Z", 774 | "quantity": 757, 775 | "unitCost": 5163.91, 776 | "totalCost": 8653.39 777 | }, 778 | { 779 | "purchaseId": "570950ee-9c94-4423-bc5d-546524adf1f3", 780 | "productId": "e5b0da8c-148d-4680-b262-8609fb8a10da", 781 | "timestamp": "2013-02-09T06:05:04Z", 782 | "quantity": 856, 783 | "unitCost": 9928.49, 784 | "totalCost": 3502.45 785 | }, 786 | { 787 | "purchaseId": "5ad8b5f9-aaee-428e-aa11-3c1991e6d703", 788 | "productId": "2be5b024-2c96-4f29-912c-c6f36353f799", 789 | "timestamp": "2000-01-23T11:04:04Z", 790 | "quantity": 879, 791 | "unitCost": 4230.1, 792 | "totalCost": 7286.78 793 | }, 794 | { 795 | "purchaseId": "6253b4e0-5089-4daa-ad09-6424516ff613", 796 | "productId": "fcf2e432-62a3-4b6f-a34d-36e42a12272e", 797 | "timestamp": "2004-07-08T16:20:29Z", 798 | "quantity": 762, 799 | "unitCost": 1296.53, 800 | "totalCost": 7583.95 801 | } 802 | ] 803 | --------------------------------------------------------------------------------