Compare commits
4 Commits
dc27377ce5
...
f387daa0af
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f387daa0af | ||
266f0970a4 | |||
510b3140fd | |||
d45ba871d8 |
@ -1,12 +1,15 @@
|
||||
/* Copyright (C) 2024 Manuel Bustillo*/
|
||||
|
||||
import { lusitana } from '@/app/ui/fonts';
|
||||
import ExpenseSummary from '@/app/ui/expenses/summary';
|
||||
|
||||
export default function Page () {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="w-full items-center justify-between">
|
||||
<h1 className={`${lusitana.className} text-2xl`}>Expenses</h1>
|
||||
<h2 className={`${lusitana.className} text-xl`}>Summary</h2>
|
||||
<ExpenseSummary />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
13
app/types.tsx
Normal file
13
app/types.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
/* Copyright (C) 2024 Manuel Bustillo*/
|
||||
|
||||
import * as HeroIcon from '@heroicons/react/24/outline'
|
||||
import { ComponentProps } from 'react'
|
||||
|
||||
type Props = {
|
||||
name: keyof typeof HeroIcon
|
||||
} & ComponentProps<typeof HeroIcon.AcademicCapIcon>
|
||||
|
||||
export const Icon = ({ name, ...props }: Props) => {
|
||||
const IconComponent = HeroIcon[name]
|
||||
return <IconComponent {...props} />
|
||||
}
|
53
app/ui/components/dashboard-cards.tsx
Normal file
53
app/ui/components/dashboard-cards.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
/* Copyright (C) 2024 Manuel Bustillo*/
|
||||
|
||||
import clsx from "clsx"
|
||||
import { Icon } from "../../types";
|
||||
import * as HeroIcon from '@heroicons/react/24/outline'
|
||||
|
||||
type Style = "green" | "blue" | "red" | "orange" | "gray"
|
||||
|
||||
const colorClasses = (style: Style) => {
|
||||
switch (style) {
|
||||
case "green":
|
||||
return "bg-green-700 hover:bg-green-800"
|
||||
case "blue":
|
||||
return "bg-blue-500 hover:bg-blue-700"
|
||||
case "red":
|
||||
return "bg-red-600 hover:bg-red-700"
|
||||
case "orange":
|
||||
return "bg-orange-600 hover:bg-orange-700"
|
||||
case "gray":
|
||||
return "bg-gray-600 hover:bg-gray-700"
|
||||
}
|
||||
}
|
||||
|
||||
export async function MainCard({ amount, title, subtitle, style, iconName }:
|
||||
{
|
||||
amount: string,
|
||||
title: string,
|
||||
subtitle?: string,
|
||||
style: Style,
|
||||
iconName: keyof typeof HeroIcon
|
||||
}) {
|
||||
|
||||
return (
|
||||
<div className={`w-80 m-1 py-2 px-6 text-white flex flex-row items-center ${colorClasses(style)}`}>
|
||||
<Icon className="m-3 h-14 w-14" name={iconName} />
|
||||
<div className="flex flex-col justify-evenly">
|
||||
<div className="text-4xl font-medium">{amount}</div>
|
||||
<div className="text-xl">{title}</div>
|
||||
<div className="text-sm">{subtitle || 'ㅤ'}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export async function SecondaryCard({ amount, title, iconName, style }: { amount: string, title: string, iconName: keyof typeof HeroIcon, style: Style }) {
|
||||
return (
|
||||
<div className={`h-12 w-80 m-1 p-2 text-white flex flex-row items-center ${colorClasses(style)}`}>
|
||||
<Icon className="m-3 h-7 w-7" name={iconName} />
|
||||
<span className="text-2xl font-medium mx-1">{amount}</span>
|
||||
<span className="text-l mx-1">{title}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
45
app/ui/expenses/summary.tsx
Normal file
45
app/ui/expenses/summary.tsx
Normal file
@ -0,0 +1,45 @@
|
||||
/* Copyright (C) 2024 Manuel Bustillo*/
|
||||
|
||||
import { MainCard, SecondaryCard } from '../components/dashboard-cards';
|
||||
|
||||
export default async function ExpenseSummary() {
|
||||
return (
|
||||
<div className="my-4">
|
||||
|
||||
<div className="flex flex-row w-full my-2">
|
||||
<MainCard style="green" amount="65000€" title="Projected" subtitle="150 guests" iconName="ArrowTrendingUpIcon" />
|
||||
|
||||
<div className="flex flex-col">
|
||||
<MainCard amount="10000€" title="Paid already" iconName="Square3Stack3DIcon" style='blue' />
|
||||
<MainCard amount="198€" title="/ guest" iconName="UserIcon" style='blue' />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<MainCard amount="78000€" title="Max." subtitle='200 guests' iconName="ChevronDoubleUpIcon" style="orange" />
|
||||
<MainCard amount="45000€" title="Min." subtitle="125 guests" iconName="ChevronDoubleDownIcon" style="green" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row w-full my-2">
|
||||
<MainCard style="blue" amount="150" title="Invites sent" iconName="UsersIcon" />
|
||||
|
||||
<div className="flex flex-col">
|
||||
<SecondaryCard amount="31%" title="confirmed (27 guests)" iconName="CheckIcon" style='green' />
|
||||
<SecondaryCard amount="5%" title="declined (8 guests)" iconName="XMarkIcon" style='red' />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col">
|
||||
<SecondaryCard amount="17%" title="tentative (14 guests)" iconName="QuestionMarkCircleIcon" style='orange' />
|
||||
<SecondaryCard amount="65%" title="awaiting (72 guests)" iconName="EllipsisHorizontalIcon" style='gray' />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row w-full my-2">
|
||||
<MainCard style="blue" amount="5" title="Table simulations" iconName="ServerStackIcon" />
|
||||
<MainCard style="blue" amount="9" title="Bus simulations" iconName="TruckIcon" />
|
||||
<MainCard style="blue" amount="98" title="QR codes" iconName="QrCodeIcon" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
"dependencies": {
|
||||
"@heroicons/react": "^2.1.4",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@vercel/postgres": "^0.9.0",
|
||||
"@vercel/postgres": "^0.10.0",
|
||||
"autoprefixer": "10.4.20",
|
||||
"bcrypt": "^5.1.1",
|
||||
"clsx": "^2.1.1",
|
||||
|
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
@ -15,8 +15,8 @@ importers:
|
||||
specifier: ^0.5.7
|
||||
version: 0.5.9(tailwindcss@3.4.14)
|
||||
'@vercel/postgres':
|
||||
specifier: ^0.9.0
|
||||
version: 0.9.0
|
||||
specifier: ^0.10.0
|
||||
version: 0.10.0(utf-8-validate@6.0.4)
|
||||
autoprefixer:
|
||||
specifier: 10.4.20
|
||||
version: 10.4.20(postcss@8.4.47)
|
||||
@ -352,9 +352,9 @@ packages:
|
||||
'@types/react@18.3.5':
|
||||
resolution: {integrity: sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==}
|
||||
|
||||
'@vercel/postgres@0.9.0':
|
||||
resolution: {integrity: sha512-WiI2g3+ce2g1u1gP41MoDj2DsMuQQ+us7vHobysRixKECGaLHpfTI7DuVZmHU087ozRAGr3GocSyqmWLLo+fig==}
|
||||
engines: {node: '>=14.6'}
|
||||
'@vercel/postgres@0.10.0':
|
||||
resolution: {integrity: sha512-fSD23DxGND40IzSkXjcFcxr53t3Tiym59Is0jSYIFpG4/0f0KO9SGtcp1sXiebvPaGe7N/tU05cH4yt2S6/IPg==}
|
||||
engines: {node: '>=18.14'}
|
||||
|
||||
abbrev@1.1.1:
|
||||
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
|
||||
@ -1465,12 +1465,13 @@ snapshots:
|
||||
'@types/prop-types': 15.7.12
|
||||
csstype: 3.1.3
|
||||
|
||||
'@vercel/postgres@0.9.0':
|
||||
'@vercel/postgres@0.10.0(utf-8-validate@6.0.4)':
|
||||
dependencies:
|
||||
'@neondatabase/serverless': 0.9.4
|
||||
bufferutil: 4.0.8
|
||||
utf-8-validate: 6.0.4
|
||||
ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
transitivePeerDependencies:
|
||||
- utf-8-validate
|
||||
|
||||
abbrev@1.1.1: {}
|
||||
|
||||
@ -2248,6 +2249,7 @@ snapshots:
|
||||
utf-8-validate@6.0.4:
|
||||
dependencies:
|
||||
node-gyp-build: 4.8.1
|
||||
optional: true
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user