diff --git a/lib/currency/formatter.ts b/lib/currency/formatter.ts new file mode 100644 index 0000000..867f841 --- /dev/null +++ b/lib/currency/formatter.ts @@ -0,0 +1,9 @@ +export function formatCurrency(price: number | bigint, locale: string = 'en-US', currency: string = 'CZK'): string { + const currencyFormatter = new Intl.NumberFormat(locale, { + style: 'currency', + currency: currency, + maximumFractionDigits: 0, + }); + + return currencyFormatter.format(price).replace(',', ' ') +} diff --git a/src/components/Button.tsx b/src/components/Button.tsx new file mode 100644 index 0000000..422fc7c --- /dev/null +++ b/src/components/Button.tsx @@ -0,0 +1,78 @@ +import Link from "next/link"; +import { ReactNode } from "react"; + +function classNames(...classes: any) { + return classes.filter(Boolean).join(' ') +} + +const baseStyles = { + solid: + "group inline-flex items-center justify-center rounded-md font-semibold focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2", + outline: + "group inline-flex ring-1 items-center justify-center rounded-md focus:outline-none", +}; + +const variantStyles = { + solid: { + black: + "bg-black text-white hover:bg-gray-700 active:bg-gray-800 focus-visible:outline-gray-900", + amber: + "bg-amber-500 text-white hover:bg-amber-600 active:bg-amber-800 focus-visible:outline-amber-500", + white: + "bg-white text-black hover:bg-amber-50 active:bg-amber-200 focus-visible:outline-white", + }, + outline: { + black: + "ring-gray-200 text-black hover:ring-gray-300 active:bg-gray-100 focus-visible:outline-amber-500 focus-visible:ring-gray-300", + white: + "ring-gray-700 text-white hover:ring-gray-500 active:ring-gray-700 focus-visible:outline-white", + amber: "", // Outline buttons cannot be amber + }, +}; + +const transitionStyle = "transition duration-150 ease-in-out"; + +const sizeStyles = { + medium: "px-4 py-2 text-sm", + large: "px-8 py-4 text-base", +}; + +type Props = { + variant?: "solid" | "outline"; + color?: "black" | "white" | "amber"; + size?: "medium" | "large"; + className?: string; + href?: string; + children?: ReactNode; +}; + +export function Button({ + variant = "solid", + color = "black", + size = "medium", + className, + href, + children, +}: Props) { + if (variant === "outline" && color === "amber") { + throw new Error("Outline buttons cannot be amber"); + } + + className = classNames( + baseStyles[variant], + variantStyles[variant][color], + sizeStyles[size], + transitionStyle, + className + ); + + if (href !== undefined) { + return ( + + {children} + + ); + } else { + return ; + } +} diff --git a/src/pages/trainings.tsx b/src/pages/trainings.tsx new file mode 100644 index 0000000..f5dd6b0 --- /dev/null +++ b/src/pages/trainings.tsx @@ -0,0 +1,114 @@ +import { GetServerSideProps } from "next"; +import Head from "next/head"; +import Link from "next/link"; + +import { formatCurrency } from "~/lib/currency/formatter"; + +import { prisma } from "~/server/db"; +import { Training } from "lib/content/training"; +import { Layout } from "~/components/Layout"; +import { Button } from "~/components/Button"; + +export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { + const trainings = await prisma.training.findMany({ + select: { + id: true, + name: true, + days: true, + draft: true, + priceOpen: true, + priceCorporate: true, + } + }); + return { props: { trainings: trainings } }; +} + +function Table({trainings}: { trainings: Training[] }) { + return ( +
+ A list of all the users in your account including their name, title, email and role. +
*/} +| + Name + | ++ Days + | ++ Draft + | ++ Price Open + | ++ Price Corporate + | ++ Edit + | +
|---|---|---|---|---|---|
| + {training.name} + | +{training.days} | +{training.draft ? 'Yes' : 'No'} | +{formatCurrency(training.priceOpen)} | +{formatCurrency(training.priceCorporate)} | ++ + Detail of {training.name} + + | +