Skip to content

Commit

Permalink
Merge pull request #33 from hackerspace-ntnu/storage-shopping-cart
Browse files Browse the repository at this point in the history
Add storage shopping cart, loan form and various improvements
  • Loading branch information
ZeroWave022 authored Oct 2, 2024
2 parents 51c35db + 3d523ef commit 64ab8ea
Show file tree
Hide file tree
Showing 55 changed files with 1,866 additions and 313 deletions.
Binary file modified bun.lockb
Binary file not shown.
2 changes: 2 additions & 0 deletions lighthouserc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ ci:
- 'http://localhost:3000/en/news'
- 'http://localhost:3000/en/news/1'
- 'http://localhost:3000/en/about'
- 'http://localhost:3000/en/storage'
- 'http://localhost:3000/en/storage/shopping-cart'
startServerCommand: 'bun run start'
upload:
target: 'lhci'
Expand Down
46 changes: 43 additions & 3 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
"next": "Next",
"goToNextPage": "Go to next page",
"morePages": "More pages",
"page": "page"
"page": "page",
"category": "category",
"sort": "sort",
"photoOf": "Photo of {name}"
},
"error": {
"notFound": "404 - Page not found",
Expand Down Expand Up @@ -60,13 +63,15 @@
},
"storage": {
"title": "Storage",
"searchPlaceholder": "Search for product...",
"card": {
"quantityInfo": "{quantity} units",
"addToCart": "Add to cart"
"addToCart": "Add to cart",
"removeFromCart": "Remove from cart"
},
"select": {
"ariaLabel": "Select how to filter the storage items",
"filters": "Filters",
"defaultPlaceholder": "Sort results",
"popularity": "Popularity",
"sortDescending": "Inventory (descending)",
"sortAscending": "Inventory (ascending)",
Expand All @@ -80,8 +85,43 @@
"peripherals": "PC peripherals",
"miniPC": "Mini PC"
},
"searchParams": {
"popularity": "popularity",
"descending": "descending",
"ascending": "ascending",
"name": "name",
"cables": "cables",
"sensors": "sensors",
"peripherals": "peripherals",
"miniPC": "minipc"
},
"tooltips": {
"viewShoppingCart": "View shopping cart"
},
"shoppingCart": {
"title": "Shopping Cart",
"productId": "Product ID",
"productName": "Product Name",
"location": "Location",
"unitsAvailable": "Units available",
"tableDescription": "A list of your shopping cart items.",
"backToStorage": "Back to storage",
"cartEmpty": "Your shopping cart is empty.",
"clearCart": "Empty shopping cart",
"cancel": "Cancel",
"clear": "Clear",
"clearCartDescription": "Are you sure you want to clear your shopping cart? All items will be removed.",
"borrowNow": "Borrow now",
"amountOfItemARIA": "Select number of this item"
},
"loanForm": {
"name": "Name",
"email": "Email",
"phoneNumber": "Phone number",
"phoneNumberDescription": "Phone number for contact. Include country code if the number isn't Norwegian.",
"returnBy": "Return by",
"returnByDescription": "Select how long you would like to borrow the item for.",
"submit": "Submit"
}
}
}
46 changes: 43 additions & 3 deletions messages/no.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
"next": "Neste",
"goToNextPage": "Gå til neste side",
"morePages": "Flere sider",
"page": "side"
"page": "side",
"category": "kategori",
"sort": "sortering",
"photoOf": "Bilde av {name}"
},
"error": {
"notFound": "404 - Siden ble ikke funnet",
Expand Down Expand Up @@ -60,13 +63,15 @@
},
"storage": {
"title": "Lager",
"searchPlaceholder": "Søk etter produkt...",
"card": {
"quantityInfo": "{quantity} stk.",
"addToCart": "Legg i handlekurven"
"addToCart": "Legg i handlekurven",
"removeFromCart": "Fjern fra handlekurven"
},
"select": {
"ariaLabel": "Velg hvordan du vil filtrere varene i lageret",
"filters": "Filtre",
"defaultPlaceholder": "Sorter resultater",
"popularity": "Popularitet",
"sortDescending": "Lagerbeholdning (synkende)",
"sortAscending": "Lagerbeholdning (stigende)",
Expand All @@ -80,8 +85,43 @@
"peripherals": "PC-tilbehør",
"miniPC": "Mini-PC"
},
"searchParams": {
"popularity": "popularitet",
"descending": "synkende",
"ascending": "stigende",
"name": "navn",
"cables": "kabler",
"sensors": "sensorer",
"peripherals": "tilbehoer",
"miniPC": "minipc"
},
"tooltips": {
"viewShoppingCart": "Vis handlekurv"
},
"shoppingCart": {
"title": "Handlekurv",
"productId": "Produkt-ID",
"productName": "Produktnavn",
"location": "Plass",
"unitsAvailable": "Stk tilgjengelig",
"tableDescription": "En liste over handlekurven din.",
"backToStorage": "Tilbake til lageret",
"cartEmpty": "Handlekurven din er tom.",
"clearCart": "Tøm handlekurven",
"cancel": "Avbryt",
"clear": "Tøm",
"clearCartDescription": "Er du sikker på at du vil tømme handlekurven? Alle varer vil bli slettet.",
"borrowNow": "Lån nå",
"amountOfItemARIA": "Velg antallet av denne gjenstanden"
},
"loanForm": {
"name": "Navn",
"email": "Epost",
"phoneNumber": "Mobilnummer",
"phoneNumberDescription": "Mobilnummer for kontakt. Inkluder landskode hvis mobilnummeret er ikke norsk.",
"returnBy": "Lån fram til",
"returnByDescription": "Velg hvor lenge du ønsker å låne gjenstanden(e)",
"submit": "Send"
}
}
}
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
},
"dependencies": {
"@aws-sdk/client-s3": "^3.637.0",
"@hookform/resolvers": "^3.9.0",
"@lucia-auth/adapter-drizzle": "^1.1.0",
"@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-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-separator": "^1.1.0",
Expand All @@ -39,6 +41,7 @@
"cmdk": "1.0.0",
"country-flag-icons": "^1.5.12",
"cva": "^1.0.0-beta.1",
"date-fns": "^4.1.0",
"drizzle-orm": "^0.33.0",
"lucia": "^3.2.0",
"lucide-react": "^0.396.0",
Expand All @@ -49,7 +52,9 @@
"nuqs": "^1.17.4",
"postgres": "^3.4.4",
"react": "^18.3.1",
"react-day-picker": "8.10.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.53.0",
"reading-time": "^1.5.0",
"server-only": "^0.0.1",
"sharp": "^0.33.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PaginationCarouselSkeleton } from '@/components/layout/PaginationCarouselSkeleton';
import { PaginationCarouselSkeleton } from '@/components/composites/PaginationCarouselSkeleton';
import { CardGridSkeleton } from '@/components/news/CardGridSkeleton';
import { ItemGridSkeleton } from '@/components/news/ItemGridSkeleton';
import { Separator } from '@/components/ui/Separator';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getTranslations, unstable_setRequestLocale } from 'next-intl/server';
import { createSearchParamsCache, parseAsInteger } from 'nuqs/server';
import { Suspense } from 'react';

import { PaginationCarousel } from '@/components/layout/PaginationCarousel';
import { PaginationCarousel } from '@/components/composites/PaginationCarousel';
import { CardGrid } from '@/components/news/CardGrid';
import { ItemGrid } from '@/components/news/ItemGrid';
import { ItemGridSkeleton } from '@/components/news/ItemGridSkeleton';
Expand Down Expand Up @@ -47,14 +47,6 @@ export default function NewsPage({
<PaginationCarousel
className='my-6'
totalPages={Math.ceil(articleData.length / 6)}
t={{
goToPreviousPage: t('goToPreviousPage'),
previous: t('previous'),
morePages: t('morePages'),
goToNextPage: t('goToNextPage'),
next: t('next'),
page: t('page'),
}}
/>
</>
);
Expand Down
87 changes: 87 additions & 0 deletions src/app/[locale]/(default)/storage/(main)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { CategorySelector } from '@/components/composites/CategorySelector';
import { SearchBar } from '@/components/composites/SearchBar';
import { SortSelector } from '@/components/composites/SortSelector';
import { SelectorsSkeleton } from '@/components/storage/SelectorsSkeleton';
import { ShoppingCartLink } from '@/components/storage/ShoppingCartLink';
import { useTranslations } from 'next-intl';
import { unstable_setRequestLocale } from 'next-intl/server';
import { Suspense } from 'react';

type StorageLayoutProps = {
children: React.ReactNode;
params: { locale: string };
};

export default function StorageLayout({
children,
params: { locale },
}: StorageLayoutProps) {
unstable_setRequestLocale(locale);
const t = useTranslations('storage');
const tUi = useTranslations('ui');

// This does not make much sense with a backend, most likely the categories in the backend will have a name in both languages and an ID
const categories = [
{
label: t('combobox.cables'),
value: t('searchParams.cables'),
},
{
label: t('combobox.sensors'),
value: t('searchParams.sensors'),
},
{
label: t('combobox.peripherals'),
value: t('searchParams.peripherals'),
},
{
label: t('combobox.miniPC'),
value: t('searchParams.miniPC'),
},
];

const filters = [
{ name: t('select.popularity'), urlName: t('searchParams.popularity') },
{ name: t('select.sortDescending'), urlName: t('searchParams.descending') },
{ name: t('select.sortAscending'), urlName: t('searchParams.ascending') },
{ name: t('select.name'), urlName: t('searchParams.name') },
];

return (
<>
<div className='relative'>
<h1 className='my-4 text-center'>{t('title')}</h1>
<ShoppingCartLink
t={{ viewShoppingCart: t('tooltips.viewShoppingCart') }}
/>
</div>
<div className='my-4 flex flex-col justify-center gap-2 lg:flex-row'>
<SearchBar
className='lg:max-w-2xl'
placeholder={t('searchPlaceholder')}
/>
<Suspense fallback={<SelectorsSkeleton />}>
<SortSelector
filters={filters}
t={{
ariaLabel: t('select.ariaLabel'),
sort: tUi('sort'),
defaultValue: t('select.popularity'),
defaultSorting: t('searchParams.popularity'),
}}
/>
<CategorySelector
categories={categories}
t={{
category: tUi('category'),
sort: tUi('sort'),
defaultDescription: t('combobox.defaultDescription'),
defaultPlaceholder: t('combobox.defaultPlaceholder'),
}}
/>
</Suspense>
</div>
{children}
</>
);
}
16 changes: 16 additions & 0 deletions src/app/[locale]/(default)/storage/(main)/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { PaginationCarouselSkeleton } from '@/components/composites/PaginationCarouselSkeleton';
import { ItemCardSkeleton } from '@/components/storage/ItemCardSkeleton';
import { useId } from 'react';

export default function StorageSkeleton() {
return (
<>
<div className='grid grid-cols-1 gap-3 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4'>
{Array.from({ length: 8 }).map(() => (
<ItemCardSkeleton key={useId()} />
))}
</div>
<PaginationCarouselSkeleton className='my-6' />
</>
);
}
54 changes: 54 additions & 0 deletions src/app/[locale]/(default)/storage/(main)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { items } from '@/mock-data/items';
import { useTranslations } from 'next-intl';
import { getTranslations, unstable_setRequestLocale } from 'next-intl/server';
import { createSearchParamsCache, parseAsInteger } from 'nuqs/server';

import { PaginationCarousel } from '@/components/composites/PaginationCarousel';
import { ItemCard } from '@/components/storage/ItemCard';

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<string, string | string[] | undefined>;
}) {
unstable_setRequestLocale(locale);
const t = useTranslations('ui');

const itemsPerPage = 12;

const searchParamsCache = createSearchParamsCache({
[t('page')]: parseAsInteger.withDefault(1),
});

const { [t('page')]: page = 1 } = searchParamsCache.parse(searchParams);

return (
<>
<div className='grid grid-cols-1 xs:grid-cols-2 gap-3 md:grid-cols-3 lg:grid-cols-4'>
{items
.slice((page - 1) * itemsPerPage, page * itemsPerPage)
.map((item) => (
<ItemCard key={item.id} item={item} />
))}
</div>
<PaginationCarousel
className='my-6'
totalPages={Math.ceil(items.length / itemsPerPage)}
/>
</>
);
}
Loading

0 comments on commit 64ab8ea

Please sign in to comment.