diff --git a/bun.lockb b/bun.lockb index 1ed5e7e..7635343 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/messages/en.json b/messages/en.json index f9b2767..0977576 100644 --- a/messages/en.json +++ b/messages/en.json @@ -39,6 +39,7 @@ "storage": "Storage", "about": "About", "shiftSchedule": "Shift Schedule", + "rules": "Rules", "changeLocale": "Change language", "toggleTheme": "Toggle theme", "light": "Light", @@ -156,5 +157,10 @@ } } } + }, + "rules": { + "title": "Rules", + "forEveryone": "For everyone", + "internal": "Internal rules" } } diff --git a/messages/no.json b/messages/no.json index 8062933..92607f5 100644 --- a/messages/no.json +++ b/messages/no.json @@ -39,6 +39,7 @@ "storage": "Lager", "about": "Om oss", "shiftSchedule": "Vaktliste", + "rules": "Rules", "changeLocale": "Bytt språk", "toggleTheme": "Bytt tema", "light": "Lys", @@ -156,5 +157,10 @@ } } } + }, + "rules": { + "title": "Regler", + "forEveryone": "For alle", + "internal": "Interne regler" } } diff --git a/src/app/[locale]/(default)/rules/(main)/layout.tsx b/src/app/[locale]/(default)/rules/(main)/layout.tsx new file mode 100644 index 0000000..a691282 --- /dev/null +++ b/src/app/[locale]/(default)/rules/(main)/layout.tsx @@ -0,0 +1,21 @@ +import { getTranslations, setRequestLocale } from 'next-intl/server'; + +type RulesLayoutProps = { + children: React.ReactNode; + params: Promise<{ locale: string }>; +}; + +export default async function RulesLayout({ + children, + params, +}: RulesLayoutProps) { + const { locale } = await params; + setRequestLocale(locale); + const t = await getTranslations('rules'); + return ( + <> +

{t('title')}

+ {children} + + ); +} diff --git a/src/app/[locale]/(default)/rules/(main)/loading.tsx b/src/app/[locale]/(default)/rules/(main)/loading.tsx new file mode 100644 index 0000000..be3cce2 --- /dev/null +++ b/src/app/[locale]/(default)/rules/(main)/loading.tsx @@ -0,0 +1,5 @@ +import { RuleCardListSkeleton } from '@/components/rules/RuleCardListSkeleton'; + +export default function RulesSkeleton() { + return ; +} diff --git a/src/app/[locale]/(default)/rules/(main)/page.tsx b/src/app/[locale]/(default)/rules/(main)/page.tsx new file mode 100644 index 0000000..32cbcfe --- /dev/null +++ b/src/app/[locale]/(default)/rules/(main)/page.tsx @@ -0,0 +1,29 @@ +import { RuleCardList } from '@/components/rules/RuleCardList'; +import { rulesMockdata } from '@/mock-data/rules'; +import { getTranslations, setRequestLocale } from 'next-intl/server'; + +export async function generateMetadata({ + params, +}: { + params: Promise<{ locale: string }>; +}) { + const { locale } = await params; + + const t = await getTranslations({ locale, namespace: 'layout' }); + + return { + title: t('rules'), + }; +} + +export default async function RulesPage({ + params, +}: { + params: Promise<{ locale: string }>; +}) { + const { locale } = await params; + + setRequestLocale(locale); + + return ; +} diff --git a/src/app/[locale]/(default)/rules/[subset]/page.tsx b/src/app/[locale]/(default)/rules/[subset]/page.tsx new file mode 100644 index 0000000..b739e7d --- /dev/null +++ b/src/app/[locale]/(default)/rules/[subset]/page.tsx @@ -0,0 +1,17 @@ +import { rulesMockdata } from '@/mock-data/rules'; +import { setRequestLocale } from 'next-intl/server'; +import { notFound } from 'next/navigation'; + +export default async function RuleSubSetPage({ + params, +}: { + params: Promise<{ locale: string }>; +}) { + const { locale } = await params; + setRequestLocale(locale); + const page = rulesMockdata.find( + (rule) => rule.id === Number.parseInt(locale), + ); + if (!page) return notFound(); + return

{page.title}

; +} diff --git a/src/components/rules/RuleCard.tsx b/src/components/rules/RuleCard.tsx new file mode 100644 index 0000000..7898aac --- /dev/null +++ b/src/components/rules/RuleCard.tsx @@ -0,0 +1,57 @@ +import { Badge } from '@/components/ui/Badge'; +import { Button } from '@/components/ui/Button'; +import { Card, CardTitle } from '@/components/ui/Card'; +import { Link } from '@/lib/locale/navigation'; +import { cx } from '@/lib/utils'; +import { useTranslations } from 'next-intl'; +import Image from 'next/image'; + +type RuleCardProps = { + className?: string; + id: number; + internal: boolean; + title: string; + photoUrl: string; +}; + +function RuleCard({ className, id, internal, title, photoUrl }: RuleCardProps) { + const t = useTranslations('rules'); + + return ( + + ); +} + +export { RuleCard }; diff --git a/src/components/rules/RuleCardList.tsx b/src/components/rules/RuleCardList.tsx new file mode 100644 index 0000000..21d42c8 --- /dev/null +++ b/src/components/rules/RuleCardList.tsx @@ -0,0 +1,55 @@ +import { RuleCard } from '@/components/rules/RuleCard'; +import { cx } from '@/lib/utils'; +import { useTranslations } from 'next-intl'; + +type RuleCardListProps = { + className?: string; + rules: Array<{ + id: number; + internal: boolean; + title: string; + photoUrl: string; + }>; +}; + +function RuleCardList({ rules }: RuleCardListProps) { + const internal = rules.filter((rule) => rule.internal); + const notInternal = rules.filter((rule) => !rule.internal); + const t = useTranslations('rules'); + const isMember = true; + + return ( +
+
+

+ {t('forEveryone')} +

+ {notInternal.map((rule) => ( + + ))} +
+
+

{t('internal')}

+ {internal.map((rule) => ( + + ))} +
+
+ ); +} + +export { RuleCardList }; diff --git a/src/components/rules/RuleCardListSkeleton.tsx b/src/components/rules/RuleCardListSkeleton.tsx new file mode 100644 index 0000000..61e90da --- /dev/null +++ b/src/components/rules/RuleCardListSkeleton.tsx @@ -0,0 +1,14 @@ +import { RuleCardSkeleton } from '@/components/rules/RuleCardSkeleton'; +import { useId } from 'react'; + +function RuleCardListSkeleton() { + return ( +
+ {Array.from({ length: 5 }).map(() => ( + + ))} +
+ ); +} + +export { RuleCardListSkeleton }; diff --git a/src/components/rules/RuleCardSkeleton.tsx b/src/components/rules/RuleCardSkeleton.tsx new file mode 100644 index 0000000..1091637 --- /dev/null +++ b/src/components/rules/RuleCardSkeleton.tsx @@ -0,0 +1,24 @@ +import { Button } from '@/components/ui/Button'; +import { Card, CardTitle } from '@/components/ui/Card'; +import { Skeleton } from '@/components/ui/Skeleton'; +import { cx } from '@/lib/utils'; + +function RuleCardSkeleton() { + return ( + + ); +} + +export { RuleCardSkeleton }; diff --git a/src/lib/locale/index.ts b/src/lib/locale/index.ts index 0166353..d57d8a2 100644 --- a/src/lib/locale/index.ts +++ b/src/lib/locale/index.ts @@ -43,6 +43,14 @@ const routing = defineRouting({ en: '/storage/shopping-cart', no: '/lager/handlekurv', }, + '/rules': { + en: '/rules', + no: '/regler', + }, + '/rules/[subset]': { + en: '/rules/[subset]', + no: '/regler/[subset]', + }, '/shift-schedule': { en: '/shift-schedule', no: '/vaktliste', diff --git a/src/mock-data/rules.ts b/src/mock-data/rules.ts new file mode 100644 index 0000000..d34d125 --- /dev/null +++ b/src/mock-data/rules.ts @@ -0,0 +1,90 @@ +const rulesMockdata = [ + { + id: 1, + internal: true, + title: 'Regler for regler', + photoUrl: 'mock.jpg', + content: + 'Reglene eksisterer av en grunn, overhold dem! • For din egen sikkerhet, andre sin sikkerhet og for at utstyr skal vare. • Regler håndheves av LabOps, Styret og Ledelsen • Si ifra hvis du ser regelbrudd. Ta ansvar. • Hvis du ikke vil si ifra selv, kan du gå via tillitsvalgt, som har taushetsplikt • Hvis reglene ikke følges, kan det føre til at man ikke får bruke utstyret, eller at man blir utestengt. • Regler kan foreslås endret og/eller fremlegges av hvem som helst, men godkjennes av styret.', + }, + { + id: 2, + internal: true, + title: 'Etiske retningslinjer', + photoUrl: 'mock.jpg', + }, + { + id: 3, + internal: false, + title: 'Regler for verkstedet', + photoUrl: 'mock.jpg', + }, + { + id: 4, + internal: true, + title: 'Regler for vakt', + photoUrl: 'mock.jpg', + }, + { + id: 5, + internal: false, + title: 'Regler for bruk av 3D-printer', + photoUrl: 'mock.jpg', + }, + { + id: 6, + internal: true, + title: 'Regler for kaffemaskin', + photoUrl: 'mock.jpg', + }, + { + id: 7, + internal: true, + title: 'Regler for utlån', + photoUrl: 'mock.jpg', + }, + { + id: 8, + internal: true, + title: 'Regler for kurs', + photoUrl: 'mock.jpg', + }, + { + id: 9, + internal: true, + title: 'Regler for arrangement', + photoUrl: 'mock.jpg', + }, + { + id: 10, + internal: false, + title: 'Regler for VR briller', + photoUrl: 'mock.jpg', + }, + { + id: 11, + internal: false, + title: 'Regler for verksted-PC', + photoUrl: 'mock.jpg', + }, + { + id: 12, + internal: true, + title: 'Regler for kjøkkenet', + photoUrl: 'mock.jpg', + }, + { + id: 13, + internal: false, + title: 'Regler for loddestasjon', + photoUrl: 'mock.jpg', + }, + { + id: 14, + internal: true, + title: 'Regler for Drive', + photoUrl: 'mock.jpg', + }, +]; + +export { rulesMockdata };