diff --git a/.vscode/settings.json b/.vscode/settings.json index 962d1d9b..91552d06 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,10 @@ { - "editor.codeActionsOnSave": { - "source.organizeImports": "always" - }, + // This config make my local change the import path with space in bracket eg: { foo, bar }, + // You need to run `npm run codecheck` to reformatting the code. + // So I comment this stuff to prevent reformatting the import code with space in bracket + // "editor.codeActionsOnSave": { + // "source.organizeImports": "always" + // }, "javascript.preferences.quoteStyle": "single", "typescript.preferences.quoteStyle": "single", "html.format.indentInnerHtml": true, diff --git a/functions/src/definitions/FieldType.ts b/functions/src/definitions/FieldType.ts new file mode 100644 index 00000000..90741b56 --- /dev/null +++ b/functions/src/definitions/FieldType.ts @@ -0,0 +1,16 @@ +/** + * Field type enum + */ +export enum FieldType { + InputText = "input-text", + TextLine = "text-line", + TextboxRich = "textbox-rich", + TextRich = "text-rich", + DatePicker = "datepicker", + Date = "date", + FileUpload = "file-upload", + Switcher = "switcher", + Radio = "radio", + Checkbox = "checkbox", + Dropdown = "dropdown" +} \ No newline at end of file diff --git a/functions/src/models/TanamDocumentField.ts b/functions/src/models/TanamDocumentField.ts index b84b21d5..0bbee834 100644 --- a/functions/src/models/TanamDocumentField.ts +++ b/functions/src/models/TanamDocumentField.ts @@ -1,10 +1,12 @@ -import {LocalizedString} from "./LocalizedString"; +import {FieldType} from './../definitions/FieldType'; +import {LocalizedString, Translations} from "./LocalizedString"; export interface ITanamDocumentField { weight: number; title: LocalizedString; description: LocalizedString; - type: string; + type: FieldType | string; + fieldType: FieldType; validators: string[] | null; } @@ -21,15 +23,17 @@ export class TanamDocumentField { constructor(id: string, json: ITanamDocumentField) { this.id = id; this.weight = json.weight; - this.title = json.title; + this.title = new LocalizedString(json.title as Translations) ?? json.title; this.description = json.description; this.type = json.type; + this.fieldType = json.fieldType; this.validators = json.validators; } public id: string; public weight: number; public title: LocalizedString; + public fieldType: FieldType; public description: LocalizedString; public readonly type: string; public readonly validators: string[] | null; @@ -43,6 +47,7 @@ export class TanamDocumentField { return { weight: this.weight, title: this.title.toJson(), + fieldType: this.fieldType, description: this.description.toJson(), type: this.type, validators: this.validators, diff --git a/hosting/package-lock.json b/hosting/package-lock.json index 97c04e60..376d546d 100644 --- a/hosting/package-lock.json +++ b/hosting/package-lock.json @@ -33,6 +33,7 @@ "@types/node": "^18.19.33", "@types/react": "^18.3.2", "@types/react-dom": "^18.3.0", + "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^7.10.0", "@typescript-eslint/parser": "^7.10.0", "autoprefixer": "^10.0.1", @@ -1640,6 +1641,12 @@ "@types/react": "*" } }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "7.10.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.10.0.tgz", diff --git a/hosting/package.json b/hosting/package.json index 9b31a75d..229d8115 100644 --- a/hosting/package.json +++ b/hosting/package.json @@ -37,6 +37,7 @@ "@types/node": "^18.19.33", "@types/react": "^18.3.2", "@types/react-dom": "^18.3.0", + "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^7.10.0", "@typescript-eslint/parser": "^7.10.0", "autoprefixer": "^10.0.1", diff --git a/hosting/src/app/(protected)/content/[documentTypeId]/[documentId]/page.tsx b/hosting/src/app/(protected)/content/[documentTypeId]/[documentId]/page.tsx index e838aa60..911912e9 100644 --- a/hosting/src/app/(protected)/content/[documentTypeId]/[documentId]/page.tsx +++ b/hosting/src/app/(protected)/content/[documentTypeId]/[documentId]/page.tsx @@ -12,16 +12,15 @@ import { TextArea, } from "@/components/Form"; import Loader from "@/components/common/Loader"; -import Notification from "@/components/common/Notification"; import PageHeader from "@/components/common/PageHeader"; +import {ErrorPage} from '@/components/Error/ErrorPage'; import {useTanamDocumentFields} from "@/hooks/useTanamDocumentFields"; import {useTanamDocumentType} from "@/hooks/useTanamDocumentTypes"; import {useTanamDocument} from "@/hooks/useTanamDocuments"; -import {UserNotification} from "@/models/UserNotification"; import {TanamDocumentField} from "@functions/models/TanamDocumentField"; import {Timestamp} from "firebase/firestore"; import {useParams, useRouter} from "next/navigation"; -import {Suspense, useEffect, useState} from "react"; +import {Suspense, useState} from "react"; const DocumentDetailsPage = () => { const router = useRouter(); @@ -31,17 +30,12 @@ const DocumentDetailsPage = () => { const {data: documentFields, error: fieldsError} = useTanamDocumentFields(documentTypeId); const [readonlyMode] = useState(true); - const [notification, setNotification] = useState(null); if (!!document?.documentType && document?.documentType !== documentTypeId) { router.push(`/content/${document?.documentType}/${document?.id}`); return ; } - useEffect(() => { - setNotification(documentError ?? typeError ?? fieldsError); - }, [documentError]); - const renderFormElement = (field: TanamDocumentField, value: any) => { const formgroupKey = `formgroup-${field.id}`; const inputKey = `input-${field.id}`; @@ -101,14 +95,24 @@ const DocumentDetailsPage = () => { } }; + if (documentError || typeError || fieldsError) { + return ( + <> + + + ); + } + return ( <> }> {documentType ? : } - {notification && ( - - )} {documentType && document && (
diff --git a/hosting/src/app/(protected)/content/[documentTypeId]/create/page.tsx b/hosting/src/app/(protected)/content/[documentTypeId]/create/page.tsx new file mode 100644 index 00000000..575a43cb --- /dev/null +++ b/hosting/src/app/(protected)/content/[documentTypeId]/create/page.tsx @@ -0,0 +1,74 @@ +"use client"; +import ContentCard from "@/components/Containers/ContentCard"; +import { + DynamicForm, + DynamicFormProps +} from "@/components/Form"; +import Loader from "@/components/common/Loader"; +import PageHeader from "@/components/common/PageHeader"; +import {ErrorPage} from '@/components/Error/ErrorPage'; +import {useTanamDocumentFields} from "@/hooks/useTanamDocumentFields"; +import {useTanamDocumentType} from "@/hooks/useTanamDocumentTypes"; +import {useParams} from "next/navigation"; +import {Suspense, useState, useEffect} from "react"; + +const DocumentCreatePage = () => { + const {documentTypeId} = useParams<{documentTypeId: string;}>() ?? {}; + const {data: documentType, error: typeError} = useTanamDocumentType(documentTypeId); + const {data: documentFields, error: fieldsError} = useTanamDocumentFields(documentTypeId); + + const [entry, setEntry] = useState({ + readonlyMode: false, + fields: [] + }); + + useEffect(() => { + if (documentFields) { + setEntry({ + ...entry, + fields: documentFields.map((field) => ({ + id: field.id, + label: field.title.translated, + placeholder: field.title.translated, + fieldType: field.fieldType, + value: "" + })) + }); + } + }, [documentFields]); + + const handleSave = (value: { [key: string]: any }) => { + console.log("handleSave ::", value); + }; + + if (typeError || fieldsError) { + return ( + <> + + + ); + } + + return ( + <> + }> + {documentType ? : } + + {documentType && ( +
+ + entry :: {JSON.stringify(entry)} + + +
+ )} + + ); +}; + +export default DocumentCreatePage; diff --git a/hosting/src/app/(protected)/content/[documentTypeId]/page.tsx b/hosting/src/app/(protected)/content/[documentTypeId]/page.tsx index 0cb670a8..541608d1 100644 --- a/hosting/src/app/(protected)/content/[documentTypeId]/page.tsx +++ b/hosting/src/app/(protected)/content/[documentTypeId]/page.tsx @@ -1,33 +1,46 @@ "use client"; import {DocumentTypeGenericList} from "@/components/DocumentType/DocumentTypeGenericList"; +import {Button} from "@/components/Button"; import Loader from "@/components/common/Loader"; -import Notification from "@/components/common/Notification"; import PageHeader from "@/components/common/PageHeader"; +import {ErrorPage} from '@/components/Error/ErrorPage'; import {useTanamDocumentType} from "@/hooks/useTanamDocumentTypes"; import {useTanamDocuments} from "@/hooks/useTanamDocuments"; -import {Suspense, useEffect, useState} from "react"; -import {useParams} from "next/navigation"; -import {UserNotification} from "@/models/UserNotification"; +import {useParams, useRouter} from "next/navigation"; +import {Suspense} from "react"; export default function DocumentTypeDocumentsPage() { + const router = useRouter(); const {documentTypeId} = useParams<{documentTypeId: string}>() ?? {}; const {data: documents, error: docsError} = useTanamDocuments(documentTypeId); - const {data: documentType} = useTanamDocumentType(documentTypeId); - const [notification, setNotification] = useState(null); + const {data: documentType, error: typesError} = useTanamDocumentType(documentTypeId); - useEffect(() => { - setNotification(docsError); - }, [docsError]); + const handleCreateNewDocumentType = () => { + router.push(`/content/${documentTypeId}/create`); + }; + + const pageActions = [ +