From 94a051460f03f6c0b577bc1ca7f61b5c95e12fd9 Mon Sep 17 00:00:00 2001 From: Manuel Bustillo Date: Sun, 11 Aug 2024 16:14:12 +0200 Subject: [PATCH 1/2] Display a tree with the groups of guests (with mocked data) --- app/dashboard/guests/page.tsx | 8 ++ app/layout.tsx | 4 + app/ui/dashboard/nav-links.tsx | 2 + app/ui/guests/affinity-groups-tree.tsx | 89 ++++++++++++++++++++++ package.json | 2 + pnpm-lock.yaml | 101 +++++++++++++++++++++++++ 6 files changed, 206 insertions(+) create mode 100644 app/ui/guests/affinity-groups-tree.tsx diff --git a/app/dashboard/guests/page.tsx b/app/dashboard/guests/page.tsx index b073632..36698c1 100644 --- a/app/dashboard/guests/page.tsx +++ b/app/dashboard/guests/page.tsx @@ -1,9 +1,17 @@ import { lusitana } from '@/app/ui/fonts'; +import AffinityGroupsTree from '@/app/ui/guests/affinity-groups-tree'; import GuestsTable from '@/app/ui/guests/table'; +import { Tree } from 'primereact/tree'; +import React, { useState, useEffect } from 'react'; + + export default function Page() { return (
+

Groups

+ +

Guests

diff --git a/app/layout.tsx b/app/layout.tsx index e18935c..992035a 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,4 +1,8 @@ import '@/app/ui/global.css' + +import 'primereact/resources/themes/lara-light-cyan/theme.css'; +import 'primeicons/primeicons.css'; + import { inter } from '@/app/ui/fonts'; export default function RootLayout({ diff --git a/app/ui/dashboard/nav-links.tsx b/app/ui/dashboard/nav-links.tsx index 82415b9..ceef053 100644 --- a/app/ui/dashboard/nav-links.tsx +++ b/app/ui/dashboard/nav-links.tsx @@ -9,6 +9,8 @@ import Link from 'next/link'; import { usePathname } from 'next/navigation'; import clsx from 'clsx'; +// Map of links to display in the side navigation. +// Depending on the size of the application, this would be stored in a database. const links = [ { name: 'Guests', href: '/dashboard/guests', icon: UserGroupIcon }, { name: 'Expenses', href: '/dashboard/expenses', icon: BanknotesIcon }, diff --git a/app/ui/guests/affinity-groups-tree.tsx b/app/ui/guests/affinity-groups-tree.tsx new file mode 100644 index 0000000..1cfeb9c --- /dev/null +++ b/app/ui/guests/affinity-groups-tree.tsx @@ -0,0 +1,89 @@ +'use client' + +import React, { useState, useEffect } from 'react'; +import { Tree } from 'primereact/tree'; +import { PrimeIcons } from 'primereact/api'; + +export default function AffinityGroupsTree() { + const [nodes, setNodes] = useState( + [ + { + key: 1, + label: "Lilly's guests", + icon: PrimeIcons.HEART, + draggable: false, + children: [ + { + key: 11, + label: "Family", + icon: PrimeIcons.HOME, + leaf: true, + className: "px-4", + }, + { + key: 12, + label: "Country club", + icon: PrimeIcons.SUN, + className: "px-4", + + }, + { + key: 13, + label: "Work", + icon: PrimeIcons.DESKTOP, + className: "px-4", + }, + ] + }, + { + key: 2, + label: "Dave's guests", + icon: PrimeIcons.HEART_FILL, + draggable: false, + children: [ + { + key: 21, + label: "Family", + icon: PrimeIcons.HOME, + leaf: true, + className: "px-4", + }, + { + key: 22, + label: "College friends", + icon: PrimeIcons.GRADUATION_CAP, + leaf: true, + className: "px-4", + }, + ] + }, + { + key: 3, + label: "Common guests", + icon: PrimeIcons.USERS, + draggable: false, + children: [ + { + key: 31, + label: "Common workplace", + icon: PrimeIcons.BRIEFCASE, + leaf: true, + className: "px-4", + }, + { + key: 32, + label: "Lifetime friends", + icon: PrimeIcons.STAR, + leaf: true, + className: "px-4", + }, + ] + } + ]); + + return ( +
+ setNodes(e.value)} className="w-full md:w-30rem" /> +
+ ) +} \ No newline at end of file diff --git a/package.json b/package.json index c013c8d..d922491 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "next": "15.0.0-canary.56", "next-auth": "5.0.0-beta.19", "postcss": "8.4.38", + "primeicons": "^7.0.0", + "primereact": "^10.8.2", "react": "19.0.0-rc-f38c22b244-20240704", "react-dom": "19.0.0-rc-f38c22b244-20240704", "tailwindcss": "3.4.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3848c0..3dcdbf8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,12 @@ importers: postcss: specifier: 8.4.38 version: 8.4.38 + primeicons: + specifier: ^7.0.0 + version: 7.0.0 + primereact: + specifier: ^10.8.2 + version: 10.8.2(@types/react@18.3.3)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704) react: specifier: 19.0.0-rc-f38c22b244-20240704 version: 19.0.0-rc-f38c22b244-20240704 @@ -87,6 +93,10 @@ packages: nodemailer: optional: true + '@babel/runtime@7.25.0': + resolution: {integrity: sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==} + engines: {node: '>=6.9.0'} + '@emnapi/runtime@1.2.0': resolution: {integrity: sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==} @@ -339,6 +349,9 @@ packages: '@types/react-dom@18.3.0': resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + '@types/react-transition-group@4.4.11': + resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==} + '@types/react@18.3.3': resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} @@ -516,6 +529,9 @@ packages: dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -648,6 +664,9 @@ packages: jose@5.4.1: resolution: {integrity: sha512-U6QajmpV/nhL9SyfAewo000fkiRQ+Yd2H0lBxJJ9apjpOgkOcBQJWOrMo917lxLptdS/n/o/xPzMkXhF46K8hQ==} + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -659,6 +678,10 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + lru-cache@10.2.2: resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} engines: {node: 14 || >=16.14} @@ -918,6 +941,23 @@ packages: pretty-format@3.8.0: resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==} + primeicons@7.0.0: + resolution: {integrity: sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==} + + primereact@10.8.2: + resolution: {integrity: sha512-bf7vktogGh0PmKT9WLDcJQoQNqqFqcAlP2crUqccnlTu63FNnQV82qEYyaFvE12Qd5qhm3EYmpsHjpf6/+olTQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -926,6 +966,15 @@ packages: peerDependencies: react: 19.0.0-rc-f38c22b244-20240704 + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + react@19.0.0-rc-f38c22b244-20240704: resolution: {integrity: sha512-OP8O6Oc1rdR9IdIKJRKaL1PYd4eGkn6f88VqiygWyyG4P4RmPPix5pp7MatqSt9TnBOcVT+lBMGoVxRgUFeudQ==} engines: {node: '>=0.10.0'} @@ -941,6 +990,9 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -1168,6 +1220,10 @@ snapshots: preact: 10.11.3 preact-render-to-string: 5.2.3(preact@10.11.3) + '@babel/runtime@7.25.0': + dependencies: + regenerator-runtime: 0.14.1 + '@emnapi/runtime@1.2.0': dependencies: tslib: 2.6.3 @@ -1374,6 +1430,10 @@ snapshots: dependencies: '@types/react': 18.3.3 + '@types/react-transition-group@4.4.11': + dependencies: + '@types/react': 18.3.3 + '@types/react@18.3.3': dependencies: '@types/prop-types': 15.7.12 @@ -1542,6 +1602,11 @@ snapshots: dlv@1.1.3: {} + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.25.0 + csstype: 3.1.3 + eastasianwidth@0.2.0: {} electron-to-chromium@1.4.789: {} @@ -1678,12 +1743,18 @@ snapshots: jose@5.4.1: {} + js-tokens@4.0.0: {} + lilconfig@2.1.0: {} lilconfig@3.1.1: {} lines-and-columns@1.2.4: {} + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + lru-cache@10.2.2: {} make-dir@3.1.0: @@ -1892,6 +1963,23 @@ snapshots: pretty-format@3.8.0: {} + primeicons@7.0.0: {} + + primereact@10.8.2(@types/react@18.3.3)(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704): + dependencies: + '@types/react-transition-group': 4.4.11 + react: 19.0.0-rc-f38c22b244-20240704 + react-dom: 19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704) + react-transition-group: 4.4.5(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704) + optionalDependencies: + '@types/react': 18.3.3 + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + queue-microtask@1.2.3: {} react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704): @@ -1899,6 +1987,17 @@ snapshots: react: 19.0.0-rc-f38c22b244-20240704 scheduler: 0.25.0-rc-f38c22b244-20240704 + react-is@16.13.1: {} + + react-transition-group@4.4.5(react-dom@19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704))(react@19.0.0-rc-f38c22b244-20240704): + dependencies: + '@babel/runtime': 7.25.0 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 19.0.0-rc-f38c22b244-20240704 + react-dom: 19.0.0-rc-f38c22b244-20240704(react@19.0.0-rc-f38c22b244-20240704) + react@19.0.0-rc-f38c22b244-20240704: {} read-cache@1.0.0: @@ -1915,6 +2014,8 @@ snapshots: dependencies: picomatch: 2.3.1 + regenerator-runtime@0.14.1: {} + resolve@1.22.8: dependencies: is-core-module: 2.13.1 From 9c2c84a45b2d5a6a85073f69bd6850ca33972080 Mon Sep 17 00:00:00 2001 From: Manuel Bustillo Date: Sun, 11 Aug 2024 17:26:16 +0200 Subject: [PATCH 2/2] Display the hierarchy of groups received from the backend --- app/ui/guests/affinity-groups-tree.tsx | 112 ++++++++----------------- app/ui/guests/table.tsx | 2 +- 2 files changed, 37 insertions(+), 77 deletions(-) diff --git a/app/ui/guests/affinity-groups-tree.tsx b/app/ui/guests/affinity-groups-tree.tsx index 1cfeb9c..95863da 100644 --- a/app/ui/guests/affinity-groups-tree.tsx +++ b/app/ui/guests/affinity-groups-tree.tsx @@ -1,89 +1,49 @@ 'use client' -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, Suspense } from 'react'; import { Tree } from 'primereact/tree'; import { PrimeIcons } from 'primereact/api'; +import { debug } from 'console'; export default function AffinityGroupsTree() { - const [nodes, setNodes] = useState( - [ - { - key: 1, - label: "Lilly's guests", - icon: PrimeIcons.HEART, - draggable: false, - children: [ - { - key: 11, - label: "Family", - icon: PrimeIcons.HOME, - leaf: true, - className: "px-4", - }, - { - key: 12, - label: "Country club", - icon: PrimeIcons.SUN, - className: "px-4", + const [nodes, setNodes] = useState([]); + const parseNode = (record: any, included: any[]) => { + if (!record.attributes) { + record = included.find((a) => a.id === record.id); + } + + const children = (record?.relationships?.children?.data || []).map((child: any) => { + return (parseNode(child, included)); + }); - }, - { - key: 13, - label: "Work", - icon: PrimeIcons.DESKTOP, - className: "px-4", - }, - ] - }, - { - key: 2, - label: "Dave's guests", - icon: PrimeIcons.HEART_FILL, - draggable: false, - children: [ - { - key: 21, - label: "Family", - icon: PrimeIcons.HOME, - leaf: true, - className: "px-4", - }, - { - key: 22, - label: "College friends", - icon: PrimeIcons.GRADUATION_CAP, - leaf: true, - className: "px-4", - }, - ] - }, - { - key: 3, - label: "Common guests", - icon: PrimeIcons.USERS, - draggable: false, - children: [ - { - key: 31, - label: "Common workplace", - icon: PrimeIcons.BRIEFCASE, - leaf: true, - className: "px-4", - }, - { - key: 32, - label: "Lifetime friends", - icon: PrimeIcons.STAR, - leaf: true, - className: "px-4", - }, - ] - } - ]); + return ({ + key: record.id, + label: record.attributes.name, + icon: record.attributes.icon, + children: children, + className: "px-4", + }) + } + + + useEffect(() => { + if (nodes.length > 0) { + return; + } + fetch("http://localhost:3001/groups.json") + .then((response) => response.json()) + .then((data) => { + setNodes(data.data.map((record: any) => { + return (parseNode(record, data.included)); + })) + }); + }); return (
- setNodes(e.value)} className="w-full md:w-30rem" /> + + setNodes(e.value)} className="w-full md:w-30rem" /> +
) } \ No newline at end of file diff --git a/app/ui/guests/table.tsx b/app/ui/guests/table.tsx index 3d90381..6340b5c 100644 --- a/app/ui/guests/table.tsx +++ b/app/ui/guests/table.tsx @@ -42,7 +42,7 @@ export default function guestsTable() { - + {guests.map((guest) => (