From b60336047120da309dbf39cbc5662542c82f88d2 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Wed, 22 May 2024 22:02:45 +0800 Subject: [PATCH 01/24] Updating menu items --- hosting/src/components/Sidebar/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hosting/src/components/Sidebar/index.tsx b/hosting/src/components/Sidebar/index.tsx index a1a76065..0522f0d7 100644 --- a/hosting/src/components/Sidebar/index.tsx +++ b/hosting/src/components/Sidebar/index.tsx @@ -117,9 +117,9 @@ const Sidebar = ({ sidebarOpen, setSidebarOpen }: SidebarProps) => { } title="Content" - isExpanded={pathname.includes("/document-types/")} + isExpanded={pathname.includes("/content/")} menuItems={documentTypes.map((doc) => ({ - href: `/document-types/${doc.id}`, + href: `/content/${doc.id}`, title: doc.title, }))} /> From 313eefa2cb30a7677fb24d8a7de70ad179162a27 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 12:48:34 +0800 Subject: [PATCH 02/24] Updating folder structure --- hosting/src/app/[site]/{tables => content/[type]}/page.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename hosting/src/app/[site]/{tables => content/[type]}/page.tsx (100%) diff --git a/hosting/src/app/[site]/tables/page.tsx b/hosting/src/app/[site]/content/[type]/page.tsx similarity index 100% rename from hosting/src/app/[site]/tables/page.tsx rename to hosting/src/app/[site]/content/[type]/page.tsx From 93833ad98f3e8ff300563a001a6c2e8755973d33 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 14:11:23 +0800 Subject: [PATCH 03/24] Implementing table components --- hosting/src/components/Table/Table.tsx | 15 +++ hosting/src/components/Table/TableHeader.tsx | 25 +++++ hosting/src/components/Table/TableRow.tsx | 17 +++ .../src/components/Table/TableRowActions.tsx | 103 ++++++++++++++++++ .../src/components/Table/TableRowLabel.tsx | 31 ++++++ hosting/src/components/Table/index.tsx | 5 + 6 files changed, 196 insertions(+) create mode 100644 hosting/src/components/Table/Table.tsx create mode 100644 hosting/src/components/Table/TableHeader.tsx create mode 100644 hosting/src/components/Table/TableRow.tsx create mode 100644 hosting/src/components/Table/TableRowActions.tsx create mode 100644 hosting/src/components/Table/TableRowLabel.tsx create mode 100644 hosting/src/components/Table/index.tsx diff --git a/hosting/src/components/Table/Table.tsx b/hosting/src/components/Table/Table.tsx new file mode 100644 index 00000000..4a0f0271 --- /dev/null +++ b/hosting/src/components/Table/Table.tsx @@ -0,0 +1,15 @@ +import React from "react"; + +interface TableProps { + children: React.ReactNode; +} + +export function Table({children}: TableProps) { + return ( +
+
+ {children}
+
+
+ ); +} diff --git a/hosting/src/components/Table/TableHeader.tsx b/hosting/src/components/Table/TableHeader.tsx new file mode 100644 index 00000000..e196b030 --- /dev/null +++ b/hosting/src/components/Table/TableHeader.tsx @@ -0,0 +1,25 @@ +import React from "react"; + +interface TableHeaderProps { + headers: React.ReactNode[]; +} + +export function TableHeader({headers}: TableHeaderProps) { + return ( + + + {headers.map((header, index) => ( + {header} + ))} + + + ); +} + +interface TableHeaderCellProps { + children: React.ReactNode; +} + +function TableHeaderCell({children}: TableHeaderCellProps) { + return {children}; +} diff --git a/hosting/src/components/Table/TableRow.tsx b/hosting/src/components/Table/TableRow.tsx new file mode 100644 index 00000000..073be0d5 --- /dev/null +++ b/hosting/src/components/Table/TableRow.tsx @@ -0,0 +1,17 @@ +import React from "react"; + +interface TableRowProps { + columns: React.ReactNode[]; +} + +export function TableRow({columns}: TableRowProps) { + return ( + + {columns.map((column, index) => ( + + {column} + + ))} + + ); +} diff --git a/hosting/src/components/Table/TableRowActions.tsx b/hosting/src/components/Table/TableRowActions.tsx new file mode 100644 index 00000000..4491427a --- /dev/null +++ b/hosting/src/components/Table/TableRowActions.tsx @@ -0,0 +1,103 @@ +import React from "react"; + +interface TableRowActionsProps { + onView?: () => void; + onDelete?: () => void; + onDownload?: () => void; +} + +export function TableRowActions({onView, onDelete, onDownload}: TableRowActionsProps) { + return ( +
+ {onView && ( + + )} + {onDelete && ( + + )} + {onDownload && ( + + )} +
+ ); +} + +function ViewIcon() { + return ( + + + + + ); +} + +function DeleteIcon() { + return ( + + + + + + + ); +} + +function DownloadIcon() { + return ( + + + + + ); +} diff --git a/hosting/src/components/Table/TableRowLabel.tsx b/hosting/src/components/Table/TableRowLabel.tsx new file mode 100644 index 00000000..10c5570b --- /dev/null +++ b/hosting/src/components/Table/TableRowLabel.tsx @@ -0,0 +1,31 @@ +import React from "react"; + +type StatusType = "success" | "danger" | "warning" | "info"; + +interface TableRowLabelProps { + title: string; + status: StatusType; +} + +export function TableRowLabel({title, status}: TableRowLabelProps) { + let colorClasses = ""; + + switch (status) { + case "success": + colorClasses = "bg-success text-success"; + break; + case "danger": + colorClasses = "bg-danger text-danger"; + break; + case "warning": + colorClasses = "bg-warning text-warning"; + break; + case "info": + colorClasses = "bg-gray-300 text-gray-700"; + break; + } + + return ( +

{title}

+ ); +} diff --git a/hosting/src/components/Table/index.tsx b/hosting/src/components/Table/index.tsx new file mode 100644 index 00000000..79fc14f8 --- /dev/null +++ b/hosting/src/components/Table/index.tsx @@ -0,0 +1,5 @@ +export {Table} from "./Table"; +export {TableRow} from "./TableRow"; +export {TableRowActions} from "./TableRowActions"; +export {TableRowLabel} from "./TableRowLabel"; +export {TableHeader} from "./TableHeader"; \ No newline at end of file From 5edf42420a85564bae689001cce09987e849c47c Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 14:21:05 +0800 Subject: [PATCH 04/24] Updating hooks and their usage --- hosting/src/app/[site]/layout.tsx | 2 +- hosting/src/components/Sidebar/index.tsx | 7 ++++--- hosting/src/hooks/useTanamDocumentTypes.tsx | 10 ++++++++-- hosting/src/hooks/useTanamSite.tsx | 4 ++-- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/hosting/src/app/[site]/layout.tsx b/hosting/src/app/[site]/layout.tsx index 9aea231f..f04b552a 100644 --- a/hosting/src/app/[site]/layout.tsx +++ b/hosting/src/app/[site]/layout.tsx @@ -15,7 +15,7 @@ interface RootLayoutProps { const RootLayout: React.FC = ({children}) => { const [loading, setLoading] = useState(true); const [errorMessage, setError] = useState(""); - const {siteData, error} = useTanamSite(); + const {data: siteData, error} = useTanamSite(); useEffect(() => { if (siteData || error) { diff --git a/hosting/src/components/Sidebar/index.tsx b/hosting/src/components/Sidebar/index.tsx index 72627f19..5478df9c 100644 --- a/hosting/src/components/Sidebar/index.tsx +++ b/hosting/src/components/Sidebar/index.tsx @@ -12,6 +12,7 @@ import {DashboardIcon} from "./icons/DashboardIcon"; import {FormsIcon} from "./icons/FormsIcon"; import {ProfileIcon} from "./icons/ProfileIcon"; import {SettingsIcon} from "./icons/SettingsIcon"; +import {useTanamSite} from "@/hooks/useTanamSite"; interface SidebarProps { sidebarOpen: boolean; @@ -20,10 +21,10 @@ interface SidebarProps { const Sidebar = ({sidebarOpen, setSidebarOpen}: SidebarProps) => { const pathname = usePathname() ?? "/"; - const site = pathname.split("/")[1]; const trigger = useRef(null); const sidebar = useRef(null); - const {data: documentTypes} = useTanamDocumentTypes(site); + const {data: documentTypes} = useTanamDocumentTypes(); + const {data: site} = useTanamSite(); const storedSidebarExpanded = "true"; @@ -89,7 +90,7 @@ const Sidebar = ({sidebarOpen, setSidebarOpen}: SidebarProps) => { title="Content" isExpanded={pathname.includes("/content/")} menuItems={documentTypes.map((doc) => ({ - href: `/content/${doc.id}`, + href: `/${site?.id}/content/${doc.id}`, title: doc.title, }))} /> diff --git a/hosting/src/hooks/useTanamDocumentTypes.tsx b/hosting/src/hooks/useTanamDocumentTypes.tsx index 6cff31a5..dda2944f 100644 --- a/hosting/src/hooks/useTanamDocumentTypes.tsx +++ b/hosting/src/hooks/useTanamDocumentTypes.tsx @@ -2,6 +2,7 @@ import {useState, useEffect} from "react"; import {firestore} from "@/firebase"; import {TanamDocumentType} from "@/models/TanamDocumentType"; import {collection, onSnapshot} from "firebase/firestore"; +import {useParams} from "next/navigation"; interface TanamDocumentTypeHook { data: TanamDocumentType[]; @@ -11,14 +12,19 @@ interface TanamDocumentTypeHook { /** * Hook to get a stream of Tanam document types * - * @param {string} site ID of the site * @return {TanamDocumentTypeHook} Hook for document types subscription */ -export function useTanamDocumentTypes(site: string): TanamDocumentTypeHook { +export function useTanamDocumentTypes(): TanamDocumentTypeHook { + const {site} = useParams<{site: string}>() ?? {site: null}; const [data, setData] = useState([]); const [error, setError] = useState(null); useEffect(() => { + if (!site) { + setError(new Error("No site parameter provided")); + return; + } + const collectionRef = collection(firestore, `tanam/${site}/document-types`); const unsubscribe = onSnapshot( diff --git a/hosting/src/hooks/useTanamSite.tsx b/hosting/src/hooks/useTanamSite.tsx index e4fcc3a7..eb525905 100644 --- a/hosting/src/hooks/useTanamSite.tsx +++ b/hosting/src/hooks/useTanamSite.tsx @@ -6,7 +6,7 @@ import {useParams} from "next/navigation"; export function useTanamSite() { const {site} = useParams<{site: string}>() ?? {site: null}; - const [siteData, setSiteData] = useState(null); + const [data, setSiteData] = useState(null); const [error, setError] = useState(null); useEffect(() => { @@ -39,5 +39,5 @@ export function useTanamSite() { } } - return {siteData, error}; + return {data, error}; } From b1fe596cb208fb7ebc490cd1146c2fa445776a62 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 14:21:54 +0800 Subject: [PATCH 05/24] Remove old tables --- hosting/src/components/Tables/TableFour.tsx | 108 -------------- hosting/src/components/Tables/TableOne.tsx | 107 -------------- hosting/src/components/Tables/TableThree.tsx | 144 ------------------- hosting/src/components/Tables/TableTwo.tsx | 95 ------------ 4 files changed, 454 deletions(-) delete mode 100644 hosting/src/components/Tables/TableFour.tsx delete mode 100644 hosting/src/components/Tables/TableOne.tsx delete mode 100644 hosting/src/components/Tables/TableThree.tsx delete mode 100644 hosting/src/components/Tables/TableTwo.tsx diff --git a/hosting/src/components/Tables/TableFour.tsx b/hosting/src/components/Tables/TableFour.tsx deleted file mode 100644 index 8cd593e7..00000000 --- a/hosting/src/components/Tables/TableFour.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import {BRAND} from "@/types/brand"; -import Image from "next/image"; -import DropdownDefault from "@/components/Dropdowns/DropdownDefault"; - -const brandData: BRAND[] = [ - { - logo: "/images/brand/brand-01.svg", - name: "Google", - visitors: 3.5, - revenues: "5,768", - sales: 590, - conversion: 4.8, - }, - { - logo: "/images/brand/brand-02.svg", - name: "Twitter", - visitors: 2.2, - revenues: "4,635", - sales: 467, - conversion: 4.3, - }, - { - logo: "/images/brand/brand-06.svg", - name: "Youtube", - visitors: 2.1, - revenues: "4,290", - sales: 420, - conversion: 3.7, - }, - { - logo: "/images/brand/brand-04.svg", - name: "Vimeo", - visitors: 1.5, - revenues: "3,580", - sales: 389, - conversion: 2.5, - }, - { - logo: "/images/brand/brand-05.svg", - name: "Facebook", - visitors: 3.5, - revenues: "6,768", - sales: 390, - conversion: 4.2, - }, -]; - -const TableFour: React.FC = () => { - return ( -
-
-
-
-

Top Channels

-
- -
- -
-
-
-
Source
-
-
-
Visitors
-
-
-
Revenues
-
-
-
Conversion
-
-
- - {brandData.map((brand, key) => ( -
-
-
- Brand -
-

{brand.name}

-
- -
-

{brand.visitors}K

-
- -
-

${brand.revenues}

-
- -
-

{brand.conversion}%

-
-
- ))} -
-
-
- ); -}; - -export default TableFour; diff --git a/hosting/src/components/Tables/TableOne.tsx b/hosting/src/components/Tables/TableOne.tsx deleted file mode 100644 index c9978335..00000000 --- a/hosting/src/components/Tables/TableOne.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import {BRAND} from "@/types/brand"; -import Image from "next/image"; - -const brandData: BRAND[] = [ - { - logo: "/images/brand/brand-01.svg", - name: "Google", - visitors: 3.5, - revenues: "5,768", - sales: 590, - conversion: 4.8, - }, - { - logo: "/images/brand/brand-02.svg", - name: "Twitter", - visitors: 2.2, - revenues: "4,635", - sales: 467, - conversion: 4.3, - }, - { - logo: "/images/brand/brand-03.svg", - name: "Github", - visitors: 2.1, - revenues: "4,290", - sales: 420, - conversion: 3.7, - }, - { - logo: "/images/brand/brand-04.svg", - name: "Vimeo", - visitors: 1.5, - revenues: "3,580", - sales: 389, - conversion: 2.5, - }, - { - logo: "/images/brand/brand-05.svg", - name: "Facebook", - visitors: 3.5, - revenues: "6,768", - sales: 390, - conversion: 4.2, - }, -]; - -const TableOne = () => { - return ( -
-

Top Channels

- -
-
-
-
Source
-
-
-
Visitors
-
-
-
Revenues
-
-
-
Sales
-
-
-
Conversion
-
-
- - {brandData.map((brand, key) => ( -
-
-
- Brand -
-

{brand.name}

-
- -
-

{brand.visitors}K

-
- -
-

${brand.revenues}

-
- -
-

{brand.sales}

-
- -
-

{brand.conversion}%

-
-
- ))} -
-
- ); -}; - -export default TableOne; diff --git a/hosting/src/components/Tables/TableThree.tsx b/hosting/src/components/Tables/TableThree.tsx deleted file mode 100644 index 85158f78..00000000 --- a/hosting/src/components/Tables/TableThree.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import {Package} from "@/types/package"; - -const packageData: Package[] = [ - { - name: "Free package", - price: 0.0, - invoiceDate: "Jan 13,2023", - status: "Paid", - }, - { - name: "Standard Package", - price: 59.0, - invoiceDate: "Jan 13,2023", - status: "Paid", - }, - { - name: "Business Package", - price: 99.0, - invoiceDate: "Jan 13,2023", - status: "Unpaid", - }, - { - name: "Standard Package", - price: 59.0, - invoiceDate: "Jan 13,2023", - status: "Pending", - }, -]; - -const TableThree = () => { - return ( -
-
- - - - - - - - - - - {packageData.map((packageItem, key) => ( - - - - - - - ))} - -
PackageInvoice dateStatusActions
-
{packageItem.name}
-

${packageItem.price}

-
-

{packageItem.invoiceDate}

-
-

- {packageItem.status} -

-
-
- - - -
-
-
-
- ); -}; - -export default TableThree; diff --git a/hosting/src/components/Tables/TableTwo.tsx b/hosting/src/components/Tables/TableTwo.tsx deleted file mode 100644 index 93324b6b..00000000 --- a/hosting/src/components/Tables/TableTwo.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import Image from "next/image"; -import {Product} from "@/types/product"; - -const productData: Product[] = [ - { - image: "/images/product/product-01.png", - name: "Apple Watch Series 7", - category: "Electronics", - price: 296, - sold: 22, - profit: 45, - }, - { - image: "/images/product/product-02.png", - name: "Macbook Pro M1", - category: "Electronics", - price: 546, - sold: 12, - profit: 125, - }, - { - image: "/images/product/product-03.png", - name: "Dell Inspiron 15", - category: "Electronics", - price: 443, - sold: 64, - profit: 247, - }, - { - image: "/images/product/product-04.png", - name: "HP Probook 450", - category: "Electronics", - price: 499, - sold: 72, - profit: 103, - }, -]; - -const TableTwo = () => { - return ( -
-
-

Top Products

-
- -
-
-

Product Name

-
-
-

Category

-
-
-

Price

-
-
-

Sold

-
-
-

Profit

-
-
- - {productData.map((product, key) => ( -
-
-
-
- Product -
-

{product.name}

-
-
-
-

{product.category}

-
-
-

${product.price}

-
-
-

{product.sold}

-
-
-

${product.profit}

-
-
- ))} -
- ); -}; - -export default TableTwo; From 61b711f751e923a4c55096ee0a0a21e798206840 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 15:05:43 +0800 Subject: [PATCH 06/24] Adding table body component --- hosting/src/components/Table/TableBody.tsx | 9 +++++++++ hosting/src/components/Table/index.tsx | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 hosting/src/components/Table/TableBody.tsx diff --git a/hosting/src/components/Table/TableBody.tsx b/hosting/src/components/Table/TableBody.tsx new file mode 100644 index 00000000..1b0234f5 --- /dev/null +++ b/hosting/src/components/Table/TableBody.tsx @@ -0,0 +1,9 @@ +import React from "react"; + +interface TableBodyProps { + children: React.ReactNode; +} + +export function TableBody({children}: TableBodyProps) { + return {children}; +} diff --git a/hosting/src/components/Table/index.tsx b/hosting/src/components/Table/index.tsx index 79fc14f8..a74942f8 100644 --- a/hosting/src/components/Table/index.tsx +++ b/hosting/src/components/Table/index.tsx @@ -2,4 +2,5 @@ export {Table} from "./Table"; export {TableRow} from "./TableRow"; export {TableRowActions} from "./TableRowActions"; export {TableRowLabel} from "./TableRowLabel"; -export {TableHeader} from "./TableHeader"; \ No newline at end of file +export {TableHeader} from "./TableHeader"; +export {TableBody} from "./TableBody"; From e5feb9e1ba681d33ceb2e292dfd3fd9b714a4a14 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 15:05:54 +0800 Subject: [PATCH 07/24] Making alert components --- hosting/src/components/common/Alerts.tsx | 82 ++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 hosting/src/components/common/Alerts.tsx diff --git a/hosting/src/components/common/Alerts.tsx b/hosting/src/components/common/Alerts.tsx new file mode 100644 index 00000000..4a6b5ea1 --- /dev/null +++ b/hosting/src/components/common/Alerts.tsx @@ -0,0 +1,82 @@ +import React from "react"; + +interface NotificationProps { + type: "warning" | "success" | "error"; + title: string; + message: string; +} + +function Notification({type, title, message}: NotificationProps) { + let colorClasses = ""; + let IconComponent = null; + + switch (type) { + case "warning": + colorClasses = "border-warning bg-warning bg-opacity-[15%] text-[#9D5425]"; + IconComponent = WarningIcon; + break; + case "success": + colorClasses = "border-[#34D399] bg-[#34D399] bg-opacity-[15%] text-black dark:text-[#34D399]"; + IconComponent = SuccessIcon; + break; + case "error": + colorClasses = "border-[#F87171] bg-[#F87171] bg-opacity-[15%] text-[#B45454]"; + IconComponent = ErrorIcon; + break; + } + + return ( +
+ {IconComponent && } +
+
{title}
+

{message}

+
+
+ ); +} + +function WarningIcon() { + return ( +
+ + + +
+ ); +} + +function SuccessIcon() { + return ( +
+ + + +
+ ); +} + +function ErrorIcon() { + return ( +
+ + + +
+ ); +} + +export default Notification; From d47f6f925f36eefa94d56a24c637a7f8b30fc7ad Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 15:06:04 +0800 Subject: [PATCH 08/24] Adding hook for tanam documents --- hosting/src/hooks/useTanamDocuments.tsx | 54 +++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 hosting/src/hooks/useTanamDocuments.tsx diff --git a/hosting/src/hooks/useTanamDocuments.tsx b/hosting/src/hooks/useTanamDocuments.tsx new file mode 100644 index 00000000..77e9d38c --- /dev/null +++ b/hosting/src/hooks/useTanamDocuments.tsx @@ -0,0 +1,54 @@ +import {useState, useEffect} from "react"; +import {firestore} from "@/firebase"; +import {collection, query, where, onSnapshot} from "firebase/firestore"; +import {useParams} from "next/navigation"; +import {TanamDocument} from "@/models/tanamDocument"; + +interface UseTanamDocumentsResult { + data: TanamDocument[]; + error: Error | null; +} + +/** + * Hook to get a stream of documents of a specific content type + * + * @param {string?} documentType Optional document type (default to content parameter from URL). + * @return {UseTanamDocumentsResult} Hook for documents subscription + */ +export function useTanamDocuments(documentType?: string): UseTanamDocumentsResult { + const {site, content} = useParams<{site: string; content: string}>() ?? {site: null, content: null}; + const type = documentType ?? content; + const [data, setData] = useState([]); + const [error, setError] = useState(null); + + useEffect(() => { + if (!site || !type) { + setError(new Error("Site or content parameter is missing")); + return; + } + + const collectionRef = collection(firestore, "tanam/documents"); + const q = query(collectionRef, where("documentType", "==", type)); + + const unsubscribe = onSnapshot( + q, + (snapshot) => { + const documents = snapshot.docs.map((doc) => + TanamDocument.fromJson({ + id: doc.id, + ...doc.data(), + }), + ); + setData(documents); + }, + (err) => { + setError(err); + }, + ); + + // Cleanup subscription on unmount + return () => unsubscribe(); + }, [site, content]); + + return {data, error}; +} From 6e565b1696143ed6994b8747850e4dcd05e2be8e Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 15:06:13 +0800 Subject: [PATCH 09/24] Updating model classes --- hosting/src/models/TanamDocumentType.ts | 22 ++++++- hosting/src/models/tanamDocument.ts | 82 +++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 hosting/src/models/tanamDocument.ts diff --git a/hosting/src/models/TanamDocumentType.ts b/hosting/src/models/TanamDocumentType.ts index 62ede89c..d503092d 100644 --- a/hosting/src/models/TanamDocumentType.ts +++ b/hosting/src/models/TanamDocumentType.ts @@ -1,13 +1,33 @@ +/** + * Tanam document type model. + */ export class TanamDocumentType { + /** + * Constructor. + * + * @param {string} id document type ID + * @param {string} title Document type title + */ constructor( - public id: string, + public readonly id: string, public title: string, ) {} + /** + * Static factory constructor. + * + * @param {any} json JSON representation of the document type + * @return {TanamDocumentType} Document type instance + */ static fromJson(json: any): TanamDocumentType { return new TanamDocumentType(json.id, json.title); } + /** + * Serialize to JSON for Firestore + * + * @return {any} JSON representation of the document type + */ toJson(): any { return { id: this.id, diff --git a/hosting/src/models/tanamDocument.ts b/hosting/src/models/tanamDocument.ts new file mode 100644 index 00000000..24908c42 --- /dev/null +++ b/hosting/src/models/tanamDocument.ts @@ -0,0 +1,82 @@ +import {FieldValue, Timestamp, serverTimestamp} from "firebase/firestore"; + +interface DocumentData { + [key: string]: any; +} + +/** + * Tanam document model. + */ +export class TanamDocument { + /** + * Constructor. + * + * @param {string} id Document ID + * @param {string | null} canonicalUrl Canonical URL of the document + * @param {DocumentData} data Data of the document + * @param {Dependency[]} dependencies Dependencies of the document + * @param {string} documentType Type of the document + * @param {Timestamp} publishedAt Date when the document was published + * @param {number} revision Revision number of the document + * @param {boolean} standalone Whether the document is standalone + * @param {string} status Status of the document + * @param {Tags} tags Tags associated with the document + * @param {Timestamp} createdAt Date when the document was created + * @param {Timestamp} updatedAt Date when the document was last updated + */ + constructor( + public readonly id: string, + public canonicalUrl: string | null, + public data: DocumentData, + public documentType: string, + public publishedAt: Timestamp, + public revision: number, + public standalone: boolean, + public status: string, + public tags: string[], + public readonly createdAt: Timestamp, + public readonly updatedAt: Timestamp, + ) {} + + /** + * Static factory constructor. + * + * @param {any} json JSON representation of the document + * @return {TanamDocument} Document instance + */ + static fromJson(json: any): TanamDocument { + return new TanamDocument( + json.id, + json.canonicalUrl, + json.data, + json.documentType, + json.published.toDate(), + json.revision, + json.standalone, + json.status, + json.tags, + json.created || json.createdAt, + json.updated || json.updatedAt, + ); + } + + /** + * Serialize to JSON for Firestore. + * + * @return {any} JSON representation of the document + */ + toJson(): any { + return { + canonicalUrl: this.canonicalUrl, + data: this.data, + documentType: this.documentType, + published: this.publishedAt, + revision: this.revision, + standalone: this.standalone, + status: this.status, + tags: this.tags, + created: this.createdAt, + updatedAt: serverTimestamp() as FieldValue, + }; + } +} From f196ab827f3eff214790c88be611f46104a16921 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 15:06:56 +0800 Subject: [PATCH 10/24] Updating page displaying documents --- .../src/app/[site]/content/[type]/page.tsx | 55 +++++++++++++------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/hosting/src/app/[site]/content/[type]/page.tsx b/hosting/src/app/[site]/content/[type]/page.tsx index 7c764b9a..324e84e2 100644 --- a/hosting/src/app/[site]/content/[type]/page.tsx +++ b/hosting/src/app/[site]/content/[type]/page.tsx @@ -1,28 +1,47 @@ +"use client"; +import React from "react"; import Breadcrumb from "@/components/Breadcrumbs/Breadcrumb"; -import TableOne from "@/components/Tables/TableOne"; -import TableThree from "@/components/Tables/TableThree"; -import TableTwo from "@/components/Tables/TableTwo"; - -import {Metadata} from "next"; +import {Table, TableBody, TableHeader, TableRow, TableRowActions, TableRowLabel} from "@/components/Table"; import DefaultLayout from "@/components/Layouts/DefaultLayout"; +import {useTanamDocuments} from "@/hooks/useTanamDocuments"; +import Alerts from "@/components/common/Alerts"; -export const metadata: Metadata = { - title: "Next.js Tables | TailAdmin - Next.js Dashboard Template", - description: "This is Next.js Tables page for TailAdmin - Next.js Tailwind CSS Admin Dashboard Template", -}; +export default function ContentOverviewPage() { + const {data: documents, error} = useTanamDocuments(); -const TablesPage = () => { return ( -
- - - -
+ {error ? ( + + ) : ( + + + + {documents.map((document, key) => ( + +
{document.id}
+ , +

{document.createdAt.toDate().toUTCString()}

, + , + console.log("View", document)} + onDelete={() => console.log("Delete", document)} + onDownload={() => console.log("Download", document)} + />, + ]} + /> + ))} +
+
+ )}
); -}; - -export default TablesPage; +} From 9ac8870bd73e0407756b9a49c4209f9656f3b368 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 15:16:31 +0800 Subject: [PATCH 11/24] Updating hooks --- hosting/src/hooks/useTanamDocumentTypes.tsx | 10 +++++----- hosting/src/hooks/useTanamDocuments.tsx | 16 ++++++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/hosting/src/hooks/useTanamDocumentTypes.tsx b/hosting/src/hooks/useTanamDocumentTypes.tsx index dda2944f..072e61e7 100644 --- a/hosting/src/hooks/useTanamDocumentTypes.tsx +++ b/hosting/src/hooks/useTanamDocumentTypes.tsx @@ -1,8 +1,8 @@ -import {useState, useEffect} from "react"; -import {firestore} from "@/firebase"; -import {TanamDocumentType} from "@/models/TanamDocumentType"; -import {collection, onSnapshot} from "firebase/firestore"; -import {useParams} from "next/navigation"; +import { firestore } from "@/firebase"; +import { TanamDocumentType } from "@/models/tanamDocumentType"; +import { collection, onSnapshot } from "firebase/firestore"; +import { useParams } from "next/navigation"; +import { useEffect, useState } from "react"; interface TanamDocumentTypeHook { data: TanamDocumentType[]; diff --git a/hosting/src/hooks/useTanamDocuments.tsx b/hosting/src/hooks/useTanamDocuments.tsx index 77e9d38c..85be9c12 100644 --- a/hosting/src/hooks/useTanamDocuments.tsx +++ b/hosting/src/hooks/useTanamDocuments.tsx @@ -16,18 +16,22 @@ interface UseTanamDocumentsResult { * @return {UseTanamDocumentsResult} Hook for documents subscription */ export function useTanamDocuments(documentType?: string): UseTanamDocumentsResult { - const {site, content} = useParams<{site: string; content: string}>() ?? {site: null, content: null}; - const type = documentType ?? content; + const {site, type: paramType} = useParams<{site: string; type: string}>() ?? {site: null, content: null}; + const type = documentType ?? paramType; const [data, setData] = useState([]); const [error, setError] = useState(null); useEffect(() => { - if (!site || !type) { - setError(new Error("Site or content parameter is missing")); + if (!site) { + setError(new Error("Site parameter is missing")); + return; + } + if (!type) { + setError(new Error("Content type parameter is missing")); return; } - const collectionRef = collection(firestore, "tanam/documents"); + const collectionRef = collection(firestore, `tanam/${site}/documents`); const q = query(collectionRef, where("documentType", "==", type)); const unsubscribe = onSnapshot( @@ -48,7 +52,7 @@ export function useTanamDocuments(documentType?: string): UseTanamDocumentsResul // Cleanup subscription on unmount return () => unsubscribe(); - }, [site, content]); + }, [site, type]); return {data, error}; } From 205f0204843fd244f3ab99ab9e94c9eec1ed214a Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 15:16:42 +0800 Subject: [PATCH 12/24] Updating package scripts --- hosting/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/package.json b/hosting/package.json index bfbaa674..7f875446 100644 --- a/hosting/package.json +++ b/hosting/package.json @@ -9,7 +9,7 @@ "lint": "next lint", "lint:fix": "next lint --fix", "prettier:fix": "prettier --write .", - "codecheck": "npm run lint:fix && npm run prettier:fix" + "codecheck": "npm run prettier:fix && npm run lint:fix" }, "dependencies": { "apexcharts": "^3.45.2", From 4fdf236461c8ed031569a7010c987b9c60484f99 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 16:41:23 +0800 Subject: [PATCH 13/24] Adding row keys --- hosting/src/app/[site]/content/[type]/page.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hosting/src/app/[site]/content/[type]/page.tsx b/hosting/src/app/[site]/content/[type]/page.tsx index 324e84e2..5b05cacd 100644 --- a/hosting/src/app/[site]/content/[type]/page.tsx +++ b/hosting/src/app/[site]/content/[type]/page.tsx @@ -23,15 +23,19 @@ export default function ContentOverviewPage() { +
{document.id}
, -

{document.createdAt.toDate().toUTCString()}

, +

+ {document.createdAt.toDate().toUTCString()} +

, , console.log("View", document)} onDelete={() => console.log("Delete", document)} onDownload={() => console.log("Download", document)} From f6be8333e07f9dad81653a0b5fe454baaf94d955 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 16:43:19 +0800 Subject: [PATCH 14/24] Adding suggested next eslint plugin --- hosting/.eslintrc.js | 9 ++++++++- hosting/package-lock.json | 23 ++++++++++++++--------- hosting/package.json | 3 ++- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/hosting/.eslintrc.js b/hosting/.eslintrc.js index b19f8749..fb8fb91b 100644 --- a/hosting/.eslintrc.js +++ b/hosting/.eslintrc.js @@ -17,7 +17,14 @@ module.exports = { es2021: true, }, - extends: ["eslint:recommended", "plugin:react/recommended", "plugin:@typescript-eslint/recommended", "google"], + extends: [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended", + "plugin:@next/next/recommended", + "google", + ], plugins: ["react", "@typescript-eslint", "prettier"], diff --git a/hosting/package-lock.json b/hosting/package-lock.json index 64f1e380..0665cf50 100644 --- a/hosting/package-lock.json +++ b/hosting/package-lock.json @@ -19,13 +19,14 @@ "react-dom": "^18.3.1" }, "devDependencies": { + "@next/eslint-plugin-next": "^14.2.3", "@types/node": "^20", "@types/react": "^18.3.2", "@types/react-dom": "^18.3.0", "@typescript-eslint/eslint-plugin": "^7.10.0", "@typescript-eslint/parser": "^7.10.0", "autoprefixer": "^10.0.1", - "eslint": "^8", + "eslint": "^8.57.0", "eslint-config-google": "^0.14.0", "eslint-config-next": "14.1.0", "eslint-config-prettier": "^9.1.0", @@ -910,11 +911,10 @@ "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.1.0.tgz", - "integrity": "sha512-x4FavbNEeXx/baD/zC/SdrvkjSby8nBn8KcCREqk6UuwvwoAPZmaV8TFCAuo/cpovBRTIY67mHhe86MQQm/68Q==", + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.3.tgz", + "integrity": "sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==", "dev": true, - "license": "MIT", "dependencies": { "glob": "10.3.10" } @@ -2555,7 +2555,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -2646,6 +2645,15 @@ } } }, + "node_modules/eslint-config-next/node_modules/@next/eslint-plugin-next": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.1.0.tgz", + "integrity": "sha512-x4FavbNEeXx/baD/zC/SdrvkjSby8nBn8KcCREqk6UuwvwoAPZmaV8TFCAuo/cpovBRTIY67mHhe86MQQm/68Q==", + "dev": true, + "dependencies": { + "glob": "10.3.10" + } + }, "node_modules/eslint-config-next/node_modules/@typescript-eslint/parser": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", @@ -2788,7 +2796,6 @@ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, - "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -2978,7 +2985,6 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", "dev": true, - "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0", "synckit": "^0.8.6" @@ -5186,7 +5192,6 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, - "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, diff --git a/hosting/package.json b/hosting/package.json index 7f875446..c3bd6ec2 100644 --- a/hosting/package.json +++ b/hosting/package.json @@ -23,13 +23,14 @@ "react-dom": "^18.3.1" }, "devDependencies": { + "@next/eslint-plugin-next": "^14.2.3", "@types/node": "^20", "@types/react": "^18.3.2", "@types/react-dom": "^18.3.0", "@typescript-eslint/eslint-plugin": "^7.10.0", "@typescript-eslint/parser": "^7.10.0", "autoprefixer": "^10.0.1", - "eslint": "^8", + "eslint": "^8.57.0", "eslint-config-google": "^0.14.0", "eslint-config-next": "14.1.0", "eslint-config-prettier": "^9.1.0", From 4fb7626d3c3d991fb6357d90bb23be9ead63aef5 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 16:44:04 +0800 Subject: [PATCH 15/24] Fixing eslint warning for switch-case-indentations --- hosting/.eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/.eslintrc.js b/hosting/.eslintrc.js index fb8fb91b..7c473da8 100644 --- a/hosting/.eslintrc.js +++ b/hosting/.eslintrc.js @@ -45,7 +45,7 @@ module.exports = { "max-len": "off", "require-jsdoc": "off", "prettier/prettier": "error", - indent: ["error", 2], + indent: ["error", 2, {SwitchCase: 1}], "operator-linebreak": ["error", "before"], quotes: "off", // Use config quotes from prettier, so we turn off this rules to avoiding conflict between eslint and prettier }, From 2703781c7010a9734450208fc5aee11d4e2e8a56 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 16:44:22 +0800 Subject: [PATCH 16/24] Formatting code --- hosting/src/hooks/useTanamDocumentTypes.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hosting/src/hooks/useTanamDocumentTypes.tsx b/hosting/src/hooks/useTanamDocumentTypes.tsx index 072e61e7..de98d5bb 100644 --- a/hosting/src/hooks/useTanamDocumentTypes.tsx +++ b/hosting/src/hooks/useTanamDocumentTypes.tsx @@ -1,8 +1,8 @@ -import { firestore } from "@/firebase"; -import { TanamDocumentType } from "@/models/tanamDocumentType"; -import { collection, onSnapshot } from "firebase/firestore"; -import { useParams } from "next/navigation"; -import { useEffect, useState } from "react"; +import {firestore} from "@/firebase"; +import {TanamDocumentType} from "@/models/tanamDocumentType"; +import {collection, onSnapshot} from "firebase/firestore"; +import {useParams} from "next/navigation"; +import {useEffect, useState} from "react"; interface TanamDocumentTypeHook { data: TanamDocumentType[]; From 1cc6fb41abc2d5ceebc94308374e5b1967420749 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 16:48:41 +0800 Subject: [PATCH 17/24] Removing deleted dependency --- hosting/src/components/Dashboard/E-commerce.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hosting/src/components/Dashboard/E-commerce.tsx b/hosting/src/components/Dashboard/E-commerce.tsx index acebacfe..00629ecc 100644 --- a/hosting/src/components/Dashboard/E-commerce.tsx +++ b/hosting/src/components/Dashboard/E-commerce.tsx @@ -4,7 +4,6 @@ import ChartOne from "@/components/Charts/ChartOne"; import ChartThree from "@/components/Charts/ChartThree"; import ChartTwo from "@/components/Charts/ChartTwo"; import ChatCard from "@/components/Chat/ChatCard"; -import TableOne from "@/components/Tables/TableOne"; import CardDataStats from "@/components/CardDataStats"; import MapOne from "@/components/Maps/MapOne"; @@ -103,9 +102,6 @@ const ECommerce: React.FC = () => { -
- -
From 06c82028aad03b5b31c4de4665e1c404448908c5 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 16:54:39 +0800 Subject: [PATCH 18/24] Code formatting --- hosting/src/hooks/useTanamDocuments.tsx | 6 +++--- hosting/src/hooks/useTanamSite.tsx | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hosting/src/hooks/useTanamDocuments.tsx b/hosting/src/hooks/useTanamDocuments.tsx index 85be9c12..ca7df33b 100644 --- a/hosting/src/hooks/useTanamDocuments.tsx +++ b/hosting/src/hooks/useTanamDocuments.tsx @@ -1,8 +1,8 @@ -import {useState, useEffect} from "react"; import {firestore} from "@/firebase"; -import {collection, query, where, onSnapshot} from "firebase/firestore"; -import {useParams} from "next/navigation"; import {TanamDocument} from "@/models/tanamDocument"; +import {collection, onSnapshot, query, where} from "firebase/firestore"; +import {useParams} from "next/navigation"; +import {useEffect, useState} from "react"; interface UseTanamDocumentsResult { data: TanamDocument[]; diff --git a/hosting/src/hooks/useTanamSite.tsx b/hosting/src/hooks/useTanamSite.tsx index eb525905..c6216865 100644 --- a/hosting/src/hooks/useTanamSite.tsx +++ b/hosting/src/hooks/useTanamSite.tsx @@ -1,8 +1,8 @@ -import {useState, useEffect} from "react"; import {firestore} from "@/firebase"; -import {doc, getDoc} from "firebase/firestore"; import {TanamSite} from "@/models/tanamSite"; +import {doc, getDoc} from "firebase/firestore"; import {useParams} from "next/navigation"; +import {useEffect, useState} from "react"; export function useTanamSite() { const {site} = useParams<{site: string}>() ?? {site: null}; From 616969dcbc3c4270f903a8fde73b683613e1aa17 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 17:03:39 +0800 Subject: [PATCH 19/24] Code check fixing --- hosting/src/hooks/useTanamDocumentTypes.tsx | 2 +- hosting/src/hooks/useTanamDocuments.tsx | 2 +- hosting/src/hooks/useTanamSite.tsx | 2 +- hosting/src/models/tanamDocument.ts | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/hosting/src/hooks/useTanamDocumentTypes.tsx b/hosting/src/hooks/useTanamDocumentTypes.tsx index de98d5bb..5347e17c 100644 --- a/hosting/src/hooks/useTanamDocumentTypes.tsx +++ b/hosting/src/hooks/useTanamDocumentTypes.tsx @@ -1,5 +1,5 @@ import {firestore} from "@/firebase"; -import {TanamDocumentType} from "@/models/tanamDocumentType"; +import {TanamDocumentType} from "@/models/TanamDocumentType"; import {collection, onSnapshot} from "firebase/firestore"; import {useParams} from "next/navigation"; import {useEffect, useState} from "react"; diff --git a/hosting/src/hooks/useTanamDocuments.tsx b/hosting/src/hooks/useTanamDocuments.tsx index ca7df33b..6f8e7333 100644 --- a/hosting/src/hooks/useTanamDocuments.tsx +++ b/hosting/src/hooks/useTanamDocuments.tsx @@ -1,5 +1,5 @@ import {firestore} from "@/firebase"; -import {TanamDocument} from "@/models/tanamDocument"; +import {TanamDocument} from "@/models/TanamDocument"; import {collection, onSnapshot, query, where} from "firebase/firestore"; import {useParams} from "next/navigation"; import {useEffect, useState} from "react"; diff --git a/hosting/src/hooks/useTanamSite.tsx b/hosting/src/hooks/useTanamSite.tsx index c6216865..02079431 100644 --- a/hosting/src/hooks/useTanamSite.tsx +++ b/hosting/src/hooks/useTanamSite.tsx @@ -1,5 +1,5 @@ import {firestore} from "@/firebase"; -import {TanamSite} from "@/models/tanamSite"; +import {TanamSite} from "@/models/TanamSite"; import {doc, getDoc} from "firebase/firestore"; import {useParams} from "next/navigation"; import {useEffect, useState} from "react"; diff --git a/hosting/src/models/tanamDocument.ts b/hosting/src/models/tanamDocument.ts index 24908c42..4195ff80 100644 --- a/hosting/src/models/tanamDocument.ts +++ b/hosting/src/models/tanamDocument.ts @@ -14,7 +14,6 @@ export class TanamDocument { * @param {string} id Document ID * @param {string | null} canonicalUrl Canonical URL of the document * @param {DocumentData} data Data of the document - * @param {Dependency[]} dependencies Dependencies of the document * @param {string} documentType Type of the document * @param {Timestamp} publishedAt Date when the document was published * @param {number} revision Revision number of the document From 60262120af124049c1351eebb46380310b0a5024 Mon Sep 17 00:00:00 2001 From: "dennis.alund" Date: Fri, 24 May 2024 09:12:30 +0000 Subject: [PATCH 20/24] Renaming files --- hosting/src/models/tanamDocument.ts | 81 ----------------------------- hosting/src/models/tanamSite.ts | 55 -------------------- 2 files changed, 136 deletions(-) delete mode 100644 hosting/src/models/tanamDocument.ts delete mode 100644 hosting/src/models/tanamSite.ts diff --git a/hosting/src/models/tanamDocument.ts b/hosting/src/models/tanamDocument.ts deleted file mode 100644 index 4195ff80..00000000 --- a/hosting/src/models/tanamDocument.ts +++ /dev/null @@ -1,81 +0,0 @@ -import {FieldValue, Timestamp, serverTimestamp} from "firebase/firestore"; - -interface DocumentData { - [key: string]: any; -} - -/** - * Tanam document model. - */ -export class TanamDocument { - /** - * Constructor. - * - * @param {string} id Document ID - * @param {string | null} canonicalUrl Canonical URL of the document - * @param {DocumentData} data Data of the document - * @param {string} documentType Type of the document - * @param {Timestamp} publishedAt Date when the document was published - * @param {number} revision Revision number of the document - * @param {boolean} standalone Whether the document is standalone - * @param {string} status Status of the document - * @param {Tags} tags Tags associated with the document - * @param {Timestamp} createdAt Date when the document was created - * @param {Timestamp} updatedAt Date when the document was last updated - */ - constructor( - public readonly id: string, - public canonicalUrl: string | null, - public data: DocumentData, - public documentType: string, - public publishedAt: Timestamp, - public revision: number, - public standalone: boolean, - public status: string, - public tags: string[], - public readonly createdAt: Timestamp, - public readonly updatedAt: Timestamp, - ) {} - - /** - * Static factory constructor. - * - * @param {any} json JSON representation of the document - * @return {TanamDocument} Document instance - */ - static fromJson(json: any): TanamDocument { - return new TanamDocument( - json.id, - json.canonicalUrl, - json.data, - json.documentType, - json.published.toDate(), - json.revision, - json.standalone, - json.status, - json.tags, - json.created || json.createdAt, - json.updated || json.updatedAt, - ); - } - - /** - * Serialize to JSON for Firestore. - * - * @return {any} JSON representation of the document - */ - toJson(): any { - return { - canonicalUrl: this.canonicalUrl, - data: this.data, - documentType: this.documentType, - published: this.publishedAt, - revision: this.revision, - standalone: this.standalone, - status: this.status, - tags: this.tags, - created: this.createdAt, - updatedAt: serverTimestamp() as FieldValue, - }; - } -} diff --git a/hosting/src/models/tanamSite.ts b/hosting/src/models/tanamSite.ts deleted file mode 100644 index a2bd2a85..00000000 --- a/hosting/src/models/tanamSite.ts +++ /dev/null @@ -1,55 +0,0 @@ -import {FieldValue, Timestamp, serverTimestamp} from "firebase/firestore"; - -/** - * Tanam site model - * This object contains all the information for the current site. - */ -export class TanamSite { - /** - * Constructor - * - * @param {string} id Site ID - * @param {string} title Site title - * @param {string} analytics Google Analytics tracking ID - * @param {Timestamp} createdAt Site creation date - * @param {Timestamp} updatedAt Site last update date - */ - constructor( - public readonly id: string, - public readonly title: string, - public readonly analytics: string, - public readonly createdAt: Timestamp, - public readonly updatedAt: Timestamp, - ) {} - - /** - * Static method to create TanamSite instance from Firestore JSON - * - * @param {any} json JSON from Firestore - * @return {TanamSite} instance of TanamSite - */ - static fromJson(json: any): TanamSite { - return new TanamSite( - json.id, - json.title, - json.analytics, - json.created || json.createdAt, - json.updated || json.updatedAt, - ); - } - - /** - * Serialize TanamSite instance to Firestore JSON - * - * @return {any} JSON representation of TanamSite - */ - toJson(): any { - return { - id: this.id, - title: this.title, - analytics: this.analytics, - createdAt: this.createdAt, - updatedAt: serverTimestamp() as FieldValue, - }; - } -} From e7b039c8b2f06375c957a7ed6e6f94dd89c69b8b Mon Sep 17 00:00:00 2001 From: "dennis.alund" Date: Fri, 24 May 2024 09:15:10 +0000 Subject: [PATCH 21/24] Adding models --- hosting/src/models/TanamDocument.ts | 81 +++++++++++++++++++++++++++++ hosting/src/models/TanamSite.ts | 55 ++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 hosting/src/models/TanamDocument.ts create mode 100644 hosting/src/models/TanamSite.ts diff --git a/hosting/src/models/TanamDocument.ts b/hosting/src/models/TanamDocument.ts new file mode 100644 index 00000000..4195ff80 --- /dev/null +++ b/hosting/src/models/TanamDocument.ts @@ -0,0 +1,81 @@ +import {FieldValue, Timestamp, serverTimestamp} from "firebase/firestore"; + +interface DocumentData { + [key: string]: any; +} + +/** + * Tanam document model. + */ +export class TanamDocument { + /** + * Constructor. + * + * @param {string} id Document ID + * @param {string | null} canonicalUrl Canonical URL of the document + * @param {DocumentData} data Data of the document + * @param {string} documentType Type of the document + * @param {Timestamp} publishedAt Date when the document was published + * @param {number} revision Revision number of the document + * @param {boolean} standalone Whether the document is standalone + * @param {string} status Status of the document + * @param {Tags} tags Tags associated with the document + * @param {Timestamp} createdAt Date when the document was created + * @param {Timestamp} updatedAt Date when the document was last updated + */ + constructor( + public readonly id: string, + public canonicalUrl: string | null, + public data: DocumentData, + public documentType: string, + public publishedAt: Timestamp, + public revision: number, + public standalone: boolean, + public status: string, + public tags: string[], + public readonly createdAt: Timestamp, + public readonly updatedAt: Timestamp, + ) {} + + /** + * Static factory constructor. + * + * @param {any} json JSON representation of the document + * @return {TanamDocument} Document instance + */ + static fromJson(json: any): TanamDocument { + return new TanamDocument( + json.id, + json.canonicalUrl, + json.data, + json.documentType, + json.published.toDate(), + json.revision, + json.standalone, + json.status, + json.tags, + json.created || json.createdAt, + json.updated || json.updatedAt, + ); + } + + /** + * Serialize to JSON for Firestore. + * + * @return {any} JSON representation of the document + */ + toJson(): any { + return { + canonicalUrl: this.canonicalUrl, + data: this.data, + documentType: this.documentType, + published: this.publishedAt, + revision: this.revision, + standalone: this.standalone, + status: this.status, + tags: this.tags, + created: this.createdAt, + updatedAt: serverTimestamp() as FieldValue, + }; + } +} diff --git a/hosting/src/models/TanamSite.ts b/hosting/src/models/TanamSite.ts new file mode 100644 index 00000000..a2bd2a85 --- /dev/null +++ b/hosting/src/models/TanamSite.ts @@ -0,0 +1,55 @@ +import {FieldValue, Timestamp, serverTimestamp} from "firebase/firestore"; + +/** + * Tanam site model + * This object contains all the information for the current site. + */ +export class TanamSite { + /** + * Constructor + * + * @param {string} id Site ID + * @param {string} title Site title + * @param {string} analytics Google Analytics tracking ID + * @param {Timestamp} createdAt Site creation date + * @param {Timestamp} updatedAt Site last update date + */ + constructor( + public readonly id: string, + public readonly title: string, + public readonly analytics: string, + public readonly createdAt: Timestamp, + public readonly updatedAt: Timestamp, + ) {} + + /** + * Static method to create TanamSite instance from Firestore JSON + * + * @param {any} json JSON from Firestore + * @return {TanamSite} instance of TanamSite + */ + static fromJson(json: any): TanamSite { + return new TanamSite( + json.id, + json.title, + json.analytics, + json.created || json.createdAt, + json.updated || json.updatedAt, + ); + } + + /** + * Serialize TanamSite instance to Firestore JSON + * + * @return {any} JSON representation of TanamSite + */ + toJson(): any { + return { + id: this.id, + title: this.title, + analytics: this.analytics, + createdAt: this.createdAt, + updatedAt: serverTimestamp() as FieldValue, + }; + } +} From 5a4fd88f1fbe2545bb9608bb169ea4a4732f8f08 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 18:04:24 +0800 Subject: [PATCH 22/24] Refactoring table component --- .../src/app/[site]/content/[type]/page.tsx | 53 ++++++++----------- hosting/src/components/Table/Table.tsx | 51 ++++++++++++++++-- hosting/src/components/Table/TableBody.tsx | 9 ---- hosting/src/components/Table/TableHeader.tsx | 25 --------- hosting/src/components/Table/TableRow.tsx | 17 ------ hosting/src/components/Table/index.tsx | 3 -- 6 files changed, 71 insertions(+), 87 deletions(-) delete mode 100644 hosting/src/components/Table/TableBody.tsx delete mode 100644 hosting/src/components/Table/TableHeader.tsx delete mode 100644 hosting/src/components/Table/TableRow.tsx diff --git a/hosting/src/app/[site]/content/[type]/page.tsx b/hosting/src/app/[site]/content/[type]/page.tsx index 5b05cacd..bcd18fa0 100644 --- a/hosting/src/app/[site]/content/[type]/page.tsx +++ b/hosting/src/app/[site]/content/[type]/page.tsx @@ -1,7 +1,7 @@ "use client"; import React from "react"; import Breadcrumb from "@/components/Breadcrumbs/Breadcrumb"; -import {Table, TableBody, TableHeader, TableRow, TableRowActions, TableRowLabel} from "@/components/Table"; +import {Table, TableRowActions, TableRowLabel} from "@/components/Table"; import DefaultLayout from "@/components/Layouts/DefaultLayout"; import {useTanamDocuments} from "@/hooks/useTanamDocuments"; import Alerts from "@/components/common/Alerts"; @@ -16,35 +16,28 @@ export default function ContentOverviewPage() { {error ? ( ) : ( - - - - {documents.map((document, key) => ( - -
{document.id}
- , -

- {document.createdAt.toDate().toUTCString()} -

, - , - console.log("View", document)} - onDelete={() => console.log("Delete", document)} - onDownload={() => console.log("Download", document)} - />, - ]} - /> - ))} -
-
+ [ +
+
{document.id}
+
, +

+ {document.createdAt.toDate().toUTCString()} +

, + , + console.log("View", document)} + onDelete={() => console.log("Delete", document)} + onDownload={() => console.log("Download", document)} + />, + ])} + /> )} ); diff --git a/hosting/src/components/Table/Table.tsx b/hosting/src/components/Table/Table.tsx index 4a0f0271..0670d0f7 100644 --- a/hosting/src/components/Table/Table.tsx +++ b/hosting/src/components/Table/Table.tsx @@ -1,14 +1,59 @@ import React from "react"; interface TableProps { - children: React.ReactNode; + headers: string[]; + rows: React.ReactNode[][]; } -export function Table({children}: TableProps) { +/** + * A Table component + * + * Example: + * ```tsx + *
[ + *
{document.id}
, + *

{document.createdAt.toDate().toUTCString()}

, + * , + * console.log("View", document)} + * onDelete={() => console.log("Delete", document)} + * onDownload={() => console.log("Download", document)} + * />, + * ])} + * /> + * ``` + * + * @param {TableProps} param0 Table parameters + * @returns + */ +export function Table({headers, rows}: TableProps) { return (
-
{children}
+ + + + {headers.map((header, index) => ( + + ))} + + + + {rows.map((row, rowIndex) => ( + + {row.map((col, colIndex) => ( + + ))} + + ))} + +
+ {header} +
+ {col} +
); diff --git a/hosting/src/components/Table/TableBody.tsx b/hosting/src/components/Table/TableBody.tsx deleted file mode 100644 index 1b0234f5..00000000 --- a/hosting/src/components/Table/TableBody.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from "react"; - -interface TableBodyProps { - children: React.ReactNode; -} - -export function TableBody({children}: TableBodyProps) { - return {children}; -} diff --git a/hosting/src/components/Table/TableHeader.tsx b/hosting/src/components/Table/TableHeader.tsx deleted file mode 100644 index e196b030..00000000 --- a/hosting/src/components/Table/TableHeader.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from "react"; - -interface TableHeaderProps { - headers: React.ReactNode[]; -} - -export function TableHeader({headers}: TableHeaderProps) { - return ( - - - {headers.map((header, index) => ( - {header} - ))} - - - ); -} - -interface TableHeaderCellProps { - children: React.ReactNode; -} - -function TableHeaderCell({children}: TableHeaderCellProps) { - return {children}; -} diff --git a/hosting/src/components/Table/TableRow.tsx b/hosting/src/components/Table/TableRow.tsx deleted file mode 100644 index 073be0d5..00000000 --- a/hosting/src/components/Table/TableRow.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from "react"; - -interface TableRowProps { - columns: React.ReactNode[]; -} - -export function TableRow({columns}: TableRowProps) { - return ( - - {columns.map((column, index) => ( - - {column} - - ))} - - ); -} diff --git a/hosting/src/components/Table/index.tsx b/hosting/src/components/Table/index.tsx index a74942f8..1eb2cc2b 100644 --- a/hosting/src/components/Table/index.tsx +++ b/hosting/src/components/Table/index.tsx @@ -1,6 +1,3 @@ export {Table} from "./Table"; -export {TableRow} from "./TableRow"; export {TableRowActions} from "./TableRowActions"; export {TableRowLabel} from "./TableRowLabel"; -export {TableHeader} from "./TableHeader"; -export {TableBody} from "./TableBody"; From b404045796787e4ef7b8f44ca7aad210855d26b1 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 18:06:43 +0800 Subject: [PATCH 23/24] Fix lint errors --- hosting/src/app/[site]/content/[type]/page.tsx | 8 ++++---- hosting/src/components/Table/Table.tsx | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hosting/src/app/[site]/content/[type]/page.tsx b/hosting/src/app/[site]/content/[type]/page.tsx index bcd18fa0..1487e384 100644 --- a/hosting/src/app/[site]/content/[type]/page.tsx +++ b/hosting/src/app/[site]/content/[type]/page.tsx @@ -19,19 +19,19 @@ export default function ContentOverviewPage() { [ -
+
{document.id}
, -

+

{document.createdAt.toDate().toUTCString()}

, , console.log("View", document)} onDelete={() => console.log("Delete", document)} onDownload={() => console.log("Download", document)} diff --git a/hosting/src/components/Table/Table.tsx b/hosting/src/components/Table/Table.tsx index 0670d0f7..5528fd9e 100644 --- a/hosting/src/components/Table/Table.tsx +++ b/hosting/src/components/Table/Table.tsx @@ -26,9 +26,9 @@ interface TableProps { * ``` * * @param {TableProps} param0 Table parameters - * @returns + * @return {JSX.Element} Table component */ -export function Table({headers, rows}: TableProps) { +export function Table({headers, rows}: TableProps): JSX.Element { return (
From 2231db0ee56d278b97006b3375380d1dc1332853 Mon Sep 17 00:00:00 2001 From: Dennis Alund Date: Fri, 24 May 2024 18:22:02 +0800 Subject: [PATCH 24/24] Fixing breadcrumb page title --- .../src/app/[site]/content/[type]/page.tsx | 13 +++-- hosting/src/hooks/useTanamDocumentTypes.tsx | 52 ++++++++++++++++++- 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/hosting/src/app/[site]/content/[type]/page.tsx b/hosting/src/app/[site]/content/[type]/page.tsx index 1487e384..e1689a20 100644 --- a/hosting/src/app/[site]/content/[type]/page.tsx +++ b/hosting/src/app/[site]/content/[type]/page.tsx @@ -6,15 +6,18 @@ import DefaultLayout from "@/components/Layouts/DefaultLayout"; import {useTanamDocuments} from "@/hooks/useTanamDocuments"; import Alerts from "@/components/common/Alerts"; -export default function ContentOverviewPage() { - const {data: documents, error} = useTanamDocuments(); +import {useTanamDocumentType} from "@/hooks/useTanamDocumentTypes"; +import Loader from "../../../../components/common/Loader"; +export default function ContentOverviewPage() { + const {data: documents, error: docsError} = useTanamDocuments(); + const {data: documentType} = useTanamDocumentType(); return ( - + {documentType ? : } - {error ? ( - + {docsError ? ( + ) : (
() ?? {site: null, type: null}; + const typeId = documentTypeId ?? paramType; + const [data, setData] = useState(null); + const [error, setError] = useState(null); + + useEffect(() => { + if (!site) { + setError(new Error("No site parameter provided")); + return; + } + if (!typeId) { + setError(new Error("Document type ID parameter is missing")); + return; + } + + const docRef = doc(firestore, `tanam/${site}/document-types`, typeId); + + const unsubscribe = onSnapshot( + docRef, + (doc) => { + if (doc.exists()) { + setData(TanamDocumentType.fromJson({id: doc.id, ...doc.data()})); + } else { + setError(new Error("Document type not found")); + } + }, + (err) => { + setError(err); + }, + ); + + // Cleanup subscription on unmount + return () => unsubscribe(); + }, [site, typeId]); + + return {data, error}; +}