diff --git a/bun.lockb b/bun.lockb
index 7b8d364..523264d 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/messages/en.json b/messages/en.json
index 4ae4606..316e4ba 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -16,6 +16,7 @@
"navigationMenu": "Navigation menu",
"news": "News",
"events": "Events",
+ "storage": "Storage",
"about": "About",
"changeLocale": "Change language",
"toggleTheme": "Toggle theme",
@@ -48,5 +49,31 @@
"newArticle": "New article",
"readTime": "{count, plural, =0 {less than a minute} one {# minute} other {# minutes}} read",
"views": "Views"
+ },
+ "storage": {
+ "title": "Storage",
+ "card": {
+ "quantityInfo": "{quantity} units",
+ "addToCart": "Add to cart"
+ },
+ "select": {
+ "filters": "Filters",
+ "defaultPlaceholder": "Sort results",
+ "popularity": "Popularity",
+ "sortDescending": "Inventory (descending)",
+ "sortAscending": "Inventory (ascending)",
+ "name": "Name (in alphabetical order)"
+ },
+ "combobox": {
+ "defaultDescription": "Choose category...",
+ "defaultPlaceholder": "Search category...",
+ "cables": "Cables",
+ "sensors": "Sensors",
+ "peripherals": "PC peripherals",
+ "miniPC": "Mini PC"
+ },
+ "tooltips": {
+ "viewShoppingCart": "View shopping cart"
+ }
}
}
diff --git a/messages/no.json b/messages/no.json
index 440eef5..79286cb 100644
--- a/messages/no.json
+++ b/messages/no.json
@@ -16,6 +16,7 @@
"navigationMenu": "Navigasjonsmeny",
"news": "Nyheter",
"events": "Hendelser",
+ "storage": "Lager",
"about": "Om oss",
"changeLocale": "Bytt språk",
"toggleTheme": "Bytt tema",
@@ -48,5 +49,31 @@
"newArticle": "Ny artikkel",
"readTime": "{count, plural, =0 {mindre enn ett minutt} one {# minutt} other {# minutter}} lesing",
"views": "Visninger"
+ },
+ "storage": {
+ "title": "Lager",
+ "card": {
+ "quantityInfo": "{quantity} stk.",
+ "addToCart": "Legg i handlekurven"
+ },
+ "select": {
+ "filters": "Filtre",
+ "defaultPlaceholder": "Sorter resultater",
+ "popularity": "Popularitet",
+ "sortDescending": "Lagerbeholdning (synkende)",
+ "sortAscending": "Lagerbeholdning (stigende)",
+ "name": "Navn (alfabetisk)"
+ },
+ "combobox": {
+ "defaultDescription": "Velg kategori...",
+ "defaultPlaceholder": "Søk etter kategori...",
+ "cables": "Kabler",
+ "sensors": "Sensorer",
+ "peripherals": "PC-tilbehør",
+ "miniPC": "Mini-PC"
+ },
+ "tooltips": {
+ "viewShoppingCart": "Vis handlekurv"
+ }
}
}
diff --git a/package.json b/package.json
index ccc2215..ff97bbe 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,8 @@
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.1",
+ "@radix-ui/react-popover": "^1.1.1",
+ "@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.1",
@@ -26,10 +28,11 @@
"@trpc/react-query": "^10.45.2",
"@trpc/server": "^10.45.2",
"autoprefixer": "^10.4.19",
+ "cmdk": "1.0.0",
"country-flag-icons": "^1.5.12",
"cva": "^1.0.0-beta.1",
"drizzle-orm": "^0.31.2",
- "lucide-react": "^0.396.0",
+ "lucide-react": "^0.429.0",
"next": "^14.2.4",
"next-intl": "^3.15.2",
"next-sitemap": "^4.2.3",
@@ -40,7 +43,7 @@
"reading-time": "^1.5.0",
"server-only": "^0.0.1",
"sharp": "^0.33.4",
- "tailwind-merge": "^2.3.0",
+ "tailwind-merge": "^2.5.2",
"zod": "^3.23.8"
},
"devDependencies": {
diff --git a/public/unknown.png b/public/unknown.png
new file mode 100644
index 0000000..8d33d4d
Binary files /dev/null and b/public/unknown.png differ
diff --git a/src/app/[locale]/(default)/storage/layout.tsx b/src/app/[locale]/(default)/storage/layout.tsx
new file mode 100644
index 0000000..c941e8f
--- /dev/null
+++ b/src/app/[locale]/(default)/storage/layout.tsx
@@ -0,0 +1,40 @@
+import { useTranslations } from 'next-intl';
+
+import { Button } from '@/components/ui/Button';
+
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from '@/components/ui/Tooltip';
+import { ShoppingCart } from 'lucide-react';
+
+export default function StorageLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ const t = useTranslations('storage');
+
+ return (
+ <>
+
+
{t('title')}
+
+
+
+
+
+
+ {t('tooltips.viewShoppingCart')}
+
+
+
+
+ {children}
+ >
+ );
+}
diff --git a/src/app/[locale]/(default)/storage/loading.tsx b/src/app/[locale]/(default)/storage/loading.tsx
new file mode 100644
index 0000000..f88b271
--- /dev/null
+++ b/src/app/[locale]/(default)/storage/loading.tsx
@@ -0,0 +1,24 @@
+import { SkeletonCard } from '@/components/storage/SkeletonCard';
+import { Skeleton } from '@/components/ui/Skeleton';
+
+export default function StorageSkeleton() {
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/app/[locale]/(default)/storage/page.tsx b/src/app/[locale]/(default)/storage/page.tsx
new file mode 100644
index 0000000..26128df
--- /dev/null
+++ b/src/app/[locale]/(default)/storage/page.tsx
@@ -0,0 +1,158 @@
+import { items } from '@/mock-data/items';
+import { useTranslations } from 'next-intl';
+import { getTranslations, unstable_setRequestLocale } from 'next-intl/server';
+import Image from 'next/image';
+import { createSearchParamsCache, parseAsInteger } from 'nuqs/parsers';
+
+import { PaginationCarousel } from '@/components/layout/PaginationCarousel';
+import { Button } from '@/components/ui/Button';
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from '@/components/ui/Card';
+import { Combobox } from '@/components/ui/Combobox';
+import { SearchBar } from '@/components/ui/SearchBar';
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '@/components/ui/Select';
+
+export async function generateMetadata({
+ params: { locale },
+}: {
+ params: { locale: string };
+}) {
+ const t = await getTranslations({ locale, namespace: 'layout' });
+
+ return {
+ title: t('storage'),
+ };
+}
+
+export default function StoragePage({
+ params: { locale },
+ searchParams,
+}: {
+ params: { locale: string };
+ searchParams: Record;
+}) {
+ unstable_setRequestLocale(locale);
+ const t = useTranslations('storage');
+ const t_ui = useTranslations('ui');
+
+ const itemsPerPage = 12;
+
+ const searchParamsCache = createSearchParamsCache({
+ [t_ui('page')]: parseAsInteger.withDefault(1),
+ });
+
+ const { [t_ui('page')]: page = 1 } = searchParamsCache.parse(searchParams);
+
+ // TODO: Implement filters and category selection
+ const categories = [
+ {
+ value: 'cables',
+ label: t('combobox.cables'),
+ },
+ {
+ value: 'sensors',
+ label: t('combobox.sensors'),
+ },
+ {
+ value: 'peripherals',
+ label: t('combobox.peripherals'),
+ },
+ {
+ value: 'miniPC',
+ label: t('combobox.miniPC'),
+ },
+ ];
+
+ const filters = [
+ 'select.popularity',
+ 'select.sortDescending',
+ 'select.sortAscending',
+ 'select.name',
+ ] as const;
+
+ return (
+ <>
+
+
+
+
+
+
+
+ {items
+ .slice((page - 1) * itemsPerPage, page * itemsPerPage)
+ .map((item) => (
+
+
+
+
+
+ {item.name}
+
+ {item.location}
+
+
+
+
+ {t('card.quantityInfo', { quantity: item.quantity })}
+
+
+
+
+ ))}
+
+
+ >
+ );
+}
diff --git a/src/components/storage/SkeletonCard.tsx b/src/components/storage/SkeletonCard.tsx
new file mode 100644
index 0000000..8c3cafb
--- /dev/null
+++ b/src/components/storage/SkeletonCard.tsx
@@ -0,0 +1,17 @@
+import { Skeleton } from '@/components/ui/Skeleton';
+
+export function SkeletonCard() {
+ return (
+
+ );
+}
diff --git a/src/components/ui/Combobox.tsx b/src/components/ui/Combobox.tsx
new file mode 100644
index 0000000..9fe99fe
--- /dev/null
+++ b/src/components/ui/Combobox.tsx
@@ -0,0 +1,91 @@
+'use client';
+
+import { Check, ChevronsUpDown } from 'lucide-react';
+import * as React from 'react';
+
+import { cx } from '@/lib/utils';
+
+import { Button } from '@/components/ui/Button';
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from '@/components/ui/Command';
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from '@/components/ui/Popover';
+
+type ComboboxProps = {
+ choices: {
+ value: string;
+ label: string;
+ }[];
+ defaultDescription: string;
+ defaultPlaceholder: string;
+ buttonClassName?: string;
+ contentClassName?: string;
+};
+
+function Combobox({
+ choices,
+ defaultDescription,
+ defaultPlaceholder,
+ buttonClassName,
+ contentClassName,
+}: ComboboxProps) {
+ const [open, setOpen] = React.useState(false);
+ const [value, setValue] = React.useState('');
+
+ return (
+
+
+
+
+
+
+
+
+ Ingen valg funnet.
+
+ {choices.map((choice) => (
+ {
+ setValue(currentValue === value ? '' : currentValue);
+ setOpen(false);
+ }}
+ >
+
+ {choice.label}
+
+ ))}
+
+
+
+
+
+ );
+}
+
+export { Combobox };
diff --git a/src/components/ui/Command.tsx b/src/components/ui/Command.tsx
new file mode 100644
index 0000000..5f651e8
--- /dev/null
+++ b/src/components/ui/Command.tsx
@@ -0,0 +1,156 @@
+'use client';
+
+import type { DialogProps } from '@radix-ui/react-dialog';
+import { Command as CommandPrimitive } from 'cmdk';
+import { Search } from 'lucide-react';
+import * as React from 'react';
+
+import { cx } from '@/lib/utils';
+
+import { Dialog, DialogContent } from '@/components/ui/Dialog';
+
+const Command = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+Command.displayName = CommandPrimitive.displayName;
+
+type CommandDialogProps = DialogProps;
+
+const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
+ return (
+
+ );
+};
+
+const CommandInput = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+));
+
+CommandInput.displayName = CommandPrimitive.Input.displayName;
+
+const CommandList = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+
+CommandList.displayName = CommandPrimitive.List.displayName;
+
+const CommandEmpty = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>((props, ref) => (
+
+));
+
+CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
+
+const CommandGroup = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+
+CommandGroup.displayName = CommandPrimitive.Group.displayName;
+
+const CommandSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
+
+const CommandItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+
+CommandItem.displayName = CommandPrimitive.Item.displayName;
+
+const CommandShortcut = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => {
+ return (
+
+ );
+};
+CommandShortcut.displayName = 'CommandShortcut';
+
+export {
+ Command,
+ CommandDialog,
+ CommandInput,
+ CommandList,
+ CommandEmpty,
+ CommandGroup,
+ CommandItem,
+ CommandShortcut,
+ CommandSeparator,
+};
diff --git a/src/components/ui/Dialog.tsx b/src/components/ui/Dialog.tsx
new file mode 100644
index 0000000..bbbab19
--- /dev/null
+++ b/src/components/ui/Dialog.tsx
@@ -0,0 +1,122 @@
+'use client';
+
+import * as DialogPrimitive from '@radix-ui/react-dialog';
+import { X } from 'lucide-react';
+import * as React from 'react';
+
+import { cx } from '@/lib/utils';
+
+const Dialog = DialogPrimitive.Root;
+
+const DialogTrigger = DialogPrimitive.Trigger;
+
+const DialogPortal = DialogPrimitive.Portal;
+
+const DialogClose = DialogPrimitive.Close;
+
+const DialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
+
+const DialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+ {children}
+
+
+ Close
+
+
+
+));
+DialogContent.displayName = DialogPrimitive.Content.displayName;
+
+const DialogHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+);
+DialogHeader.displayName = 'DialogHeader';
+
+const DialogFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+);
+DialogFooter.displayName = 'DialogFooter';
+
+const DialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogTitle.displayName = DialogPrimitive.Title.displayName;
+
+const DialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogDescription.displayName = DialogPrimitive.Description.displayName;
+
+export {
+ Dialog,
+ DialogPortal,
+ DialogOverlay,
+ DialogClose,
+ DialogTrigger,
+ DialogContent,
+ DialogHeader,
+ DialogFooter,
+ DialogTitle,
+ DialogDescription,
+};
diff --git a/src/components/ui/Input.tsx b/src/components/ui/Input.tsx
new file mode 100644
index 0000000..a16957e
--- /dev/null
+++ b/src/components/ui/Input.tsx
@@ -0,0 +1,24 @@
+import * as React from 'react';
+
+import { cx } from '@/lib/utils';
+
+export type InputProps = React.InputHTMLAttributes;
+
+const Input = React.forwardRef(
+ ({ className, type, ...props }, ref) => {
+ return (
+
+ );
+ },
+);
+Input.displayName = 'Input';
+
+export { Input };
diff --git a/src/components/ui/Popover.tsx b/src/components/ui/Popover.tsx
new file mode 100644
index 0000000..4970f8e
--- /dev/null
+++ b/src/components/ui/Popover.tsx
@@ -0,0 +1,31 @@
+'use client';
+
+import * as PopoverPrimitive from '@radix-ui/react-popover';
+import * as React from 'react';
+
+import { cx } from '@/lib/utils';
+
+const Popover = PopoverPrimitive.Root;
+
+const PopoverTrigger = PopoverPrimitive.Trigger;
+
+const PopoverContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
+
+
+
+));
+PopoverContent.displayName = PopoverPrimitive.Content.displayName;
+
+export { Popover, PopoverTrigger, PopoverContent };
diff --git a/src/components/ui/SearchBar.tsx b/src/components/ui/SearchBar.tsx
new file mode 100644
index 0000000..f4a6cdc
--- /dev/null
+++ b/src/components/ui/SearchBar.tsx
@@ -0,0 +1,37 @@
+import { Search } from 'lucide-react';
+import * as React from 'react';
+
+import { cx } from '@/lib/utils';
+
+type SearchBarProps = React.InputHTMLAttributes;
+
+/**
+ * This component creates a full search bar with an icon.
+ * The ref, if used, is passed onto the input element, not the wrapper for the component.
+ */
+const SearchBar = React.forwardRef(
+ ({ className, type, ...props }, ref) => {
+ return (
+
+
+
+
+ );
+ },
+);
+SearchBar.displayName = 'SearchBar';
+
+export { SearchBar };
diff --git a/src/components/ui/Select.tsx b/src/components/ui/Select.tsx
new file mode 100644
index 0000000..7c33ff2
--- /dev/null
+++ b/src/components/ui/Select.tsx
@@ -0,0 +1,160 @@
+'use client';
+
+import * as SelectPrimitive from '@radix-ui/react-select';
+import { Check, ChevronDown, ChevronUp } from 'lucide-react';
+import * as React from 'react';
+
+import { cx } from '@/lib/utils';
+
+const Select = SelectPrimitive.Root;
+
+const SelectGroup = SelectPrimitive.Group;
+
+const SelectValue = SelectPrimitive.Value;
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+ span]:line-clamp-1',
+ className,
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+
+));
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
+
+const SelectScrollUpButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+));
+SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
+
+const SelectScrollDownButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+));
+SelectScrollDownButton.displayName =
+ SelectPrimitive.ScrollDownButton.displayName;
+
+const SelectContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, position = 'popper', ...props }, ref) => (
+
+
+
+
+ {children}
+
+
+
+
+));
+SelectContent.displayName = SelectPrimitive.Content.displayName;
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+SelectLabel.displayName = SelectPrimitive.Label.displayName;
+
+const SelectItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+
+ {children}
+
+));
+SelectItem.displayName = SelectPrimitive.Item.displayName;
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
+
+export {
+ Select,
+ SelectGroup,
+ SelectValue,
+ SelectTrigger,
+ SelectContent,
+ SelectLabel,
+ SelectItem,
+ SelectSeparator,
+ SelectScrollUpButton,
+ SelectScrollDownButton,
+};
diff --git a/src/lib/config.ts b/src/lib/config.ts
index b839efa..1ea67ef 100644
--- a/src/lib/config.ts
+++ b/src/lib/config.ts
@@ -26,6 +26,10 @@ const pathnames = {
en: '/news/[article]',
no: '/nyheter/[article]',
},
+ '/storage': {
+ en: '/storage',
+ no: '/lager',
+ },
'/about': {
en: '/about',
no: '/om-oss',
diff --git a/src/mock-data/items.ts b/src/mock-data/items.ts
new file mode 100644
index 0000000..ce106f9
--- /dev/null
+++ b/src/mock-data/items.ts
@@ -0,0 +1,144 @@
+const items = [
+ {
+ name: 'Laptop',
+ photo_url: 'https://example.com/photos/laptop.jpg',
+ status: 'Operational',
+ quantity: 15,
+ location: 'Storage Room A',
+ },
+ {
+ name: 'Desktop PC',
+ photo_url: 'https://example.com/photos/desktop_pc.jpg',
+ status: 'Operational',
+ quantity: 10,
+ location: 'Workstation Area 1',
+ },
+ {
+ name: 'Monitor',
+ photo_url: 'https://example.com/photos/monitor.jpg',
+ status: 'Operational',
+ quantity: 20,
+ location: 'Storage Room B',
+ },
+ {
+ name: 'Keyboard',
+ photo_url: 'https://example.com/photos/keyboard.jpg',
+ status: 'Operational',
+ quantity: 50,
+ location: 'Storage Room A',
+ },
+ {
+ name: 'Mouse',
+ photo_url: 'https://example.com/photos/mouse.jpg',
+ status: 'Operational',
+ quantity: 50,
+ location: 'Storage Room A',
+ },
+ {
+ name: 'Router',
+ photo_url: 'https://example.com/photos/router.jpg',
+ status: 'Operational',
+ quantity: 5,
+ location: 'Networking Room',
+ },
+ {
+ name: 'Ethernet Cable',
+ photo_url: 'https://example.com/photos/ethernet_cable.jpg',
+ status: 'Operational',
+ quantity: 100,
+ location: 'Networking Room',
+ },
+ {
+ name: 'External Hard Drive',
+ photo_url: 'https://example.com/photos/external_hard_drive.jpg',
+ status: 'Operational',
+ quantity: 25,
+ location: 'Storage Room B',
+ },
+ {
+ name: 'USB Flash Drive',
+ photo_url: 'https://example.com/photos/usb_flash_drive.jpg',
+ status: 'Operational',
+ quantity: 75,
+ location: 'Storage Room B',
+ },
+ {
+ name: 'Power Supply Unit (PSU)',
+ photo_url: 'https://example.com/photos/psu.jpg',
+ status: 'Operational',
+ quantity: 30,
+ location: 'Storage Room C',
+ },
+ {
+ name: 'Graphics Card',
+ photo_url: 'https://example.com/photos/graphics_card.jpg',
+ status: 'Operational',
+ quantity: 12,
+ location: 'Storage Room C',
+ },
+ {
+ name: 'RAM Module',
+ photo_url: 'https://example.com/photos/ram_module.jpg',
+ status: 'Operational',
+ quantity: 40,
+ location: 'Storage Room C',
+ },
+ {
+ name: 'Motherboard',
+ photo_url: 'https://example.com/photos/motherboard.jpg',
+ status: 'Operational',
+ quantity: 10,
+ location: 'Storage Room C',
+ },
+ {
+ name: 'CPU',
+ photo_url: 'https://example.com/photos/cpu.jpg',
+ status: 'Operational',
+ quantity: 10,
+ location: 'Storage Room C',
+ },
+ {
+ name: 'SSD',
+ photo_url: 'https://example.com/photos/ssd.jpg',
+ status: 'Operational',
+ quantity: 20,
+ location: 'Storage Room C',
+ },
+ {
+ name: 'Network Switch',
+ photo_url: 'https://example.com/photos/network_switch.jpg',
+ status: 'Operational',
+ quantity: 5,
+ location: 'Networking Room',
+ },
+ {
+ name: 'Soldering Iron',
+ photo_url: 'https://example.com/photos/soldering_iron.jpg',
+ status: 'Operational',
+ quantity: 8,
+ location: 'Repair Station',
+ },
+ {
+ name: 'Multimeter',
+ photo_url: 'https://example.com/photos/multimeter.jpg',
+ status: 'Operational',
+ quantity: 10,
+ location: 'Repair Station',
+ },
+ {
+ name: 'Screwdriver Set',
+ photo_url: 'https://example.com/photos/screwdriver_set.jpg',
+ status: 'Operational',
+ quantity: 20,
+ location: 'Toolbox 1',
+ },
+ {
+ name: 'Anti-static Wrist Strap',
+ photo_url: 'https://example.com/photos/anti_static_wrist_strap.jpg',
+ status: 'Operational',
+ quantity: 15,
+ location: 'Toolbox 2',
+ },
+];
+
+export { items };