diff --git a/backend/src/admin/admin.service.ts b/backend/src/admin/admin.service.ts index 81735956..092fda38 100644 --- a/backend/src/admin/admin.service.ts +++ b/backend/src/admin/admin.service.ts @@ -110,8 +110,6 @@ export class AdminService { console.log(err.response.data); throw new Error('No users found'); }); - console.log('searchData'); - console.log(searchData); const firstName = searchData[0].firstName ? searchData[0].firstName : ''; const lastName = searchData[0].lastName ? searchData[0].lastName : ''; const username = searchData[0].attributes diff --git a/backend/src/app.controller.ts b/backend/src/app.controller.ts index ca672907..c715d0db 100644 --- a/backend/src/app.controller.ts +++ b/backend/src/app.controller.ts @@ -261,7 +261,6 @@ export class AppController { getHello(): string { return this.appService.getHello(); } - @Get() getHello2(): string { return this.appService.getHello(); diff --git a/backend/src/report/report.service.ts b/backend/src/report/report.service.ts index 3c394eca..ce69e19e 100644 --- a/backend/src/report/report.service.ts +++ b/backend/src/report/report.service.ts @@ -463,7 +463,20 @@ export class ReportService { // Format the raw ttls data const tenantAddr = rawData.tenantAddr; - const interestParcel = rawData.interestParcel[0]; + const interestParcels = rawData.interestParcel; + let concatLegalDescriptions = ''; + if (interestParcels && interestParcels.length > 0) { + interestParcels.sort((a, b) => b.interestParcelId - a.interestParcelId); + let legalDescArray = []; + for (let ip of interestParcels) { + if (ip.legalDescription && ip.legalDescription != '') { + legalDescArray.push(ip.legalDescription); + } + } + if (legalDescArray.length > 0) { + concatLegalDescriptions = legalDescArray.join('\n'); + } + } const DB_Address_Mailing_Tenant = tenantAddr[0] ? nfrAddressBuilder(tenantAddr) : ''; @@ -647,7 +660,7 @@ export class ReportService { DB_Tenure_Type: rawData.type // convert a tenure type like LICENSE to License ? rawData.type.toLowerCase().charAt(0).toUpperCase() + rawData.type.toLowerCase().slice(1) : '', - DB_Legal_Description: interestParcel ? interestParcel.legalDescription : '', + DB_Legal_Description: concatLegalDescriptions, DB_Fee_Payable_Type: DB_Fee_Payable_Type, DB_Fee_Payable_Amount_GST: DB_Fee_Payable_Amount_GST == 0 ? '' : formatMoney(DB_Fee_Payable_Amount_GST), DB_Fee_Payable_Amount: formatMoney(DB_Fee_Payable_Amount), diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1d9b5ad2..19bf305c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -30,7 +30,8 @@ "web-vitals": "^2.1.4" }, "devDependencies": { - "@babel/plugin-proposal-private-property-in-object": "^7.21.11" + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "sass": "^1.71.1" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -9656,6 +9657,12 @@ "url": "https://opencollective.com/immer" } }, + "node_modules/immutable": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", + "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", + "devOptional": true + }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -15958,6 +15965,23 @@ "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" }, + "node_modules/sass": { + "version": "1.71.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz", + "integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==", + "devOptional": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/sass-loader": { "version": "12.6.0", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", @@ -25522,6 +25546,12 @@ "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==" }, + "immutable": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", + "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", + "devOptional": true + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -29872,6 +29902,17 @@ "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" }, + "sass": { + "version": "1.71.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz", + "integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==", + "devOptional": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, "sass-loader": { "version": "12.6.0", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 91f12c85..b66f7eb5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,7 +25,8 @@ "web-vitals": "^2.1.4" }, "devDependencies": { - "@babel/plugin-proposal-private-property-in-object": "^7.21.11" + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "sass": "^1.71.1" }, "scripts": { "start": "react-scripts start", diff --git a/frontend/public/css/new.css b/frontend/public/css/new.css index 049d5e71..9f125ba2 100644 --- a/frontend/public/css/new.css +++ b/frontend/public/css/new.css @@ -17,3 +17,7 @@ hr { background-color: #000000; height: 1px; } + +.boldText { + font-weight: bold; +} diff --git a/frontend/public/js/manage-templates.js b/frontend/public/js/manage-templates.js deleted file mode 100644 index 34a9a8bd..00000000 --- a/frontend/public/js/manage-templates.js +++ /dev/null @@ -1,1339 +0,0 @@ -var documentTable; // main template upload table -var groupMaxTable, provisionTable, editProvisionVariableTable; -var documentTable2, documentTable3, documentTable4, documentTable5; // nfr variants -var reportType = ''; -var reportTitle = ''; -var nfrDelayed = 'NOTICE OF FINAL REVIEW (DELAYED)'; -var nfrNoFees = 'NOTICE OF FINAL REVIEW (NO FEES)'; -var nfrSurveyReq = 'NOTICE OF FINAL REVIEW (SURVEY REQUIRED)'; -var nfrToObtain = 'NOTICE OF FINAL REVIEW (TO OBTAIN SURVEY)'; -$(document).ready(function () { - const urlParams = new URLSearchParams(window.location.search); - const reportIndex = parseInt(urlParams.get('report')); - if (reportIndex != 2) { - $('.nofr-section').hide(); - } - switch (reportIndex) { - case 1: - reportType = 'LAND USE REPORT'; - reportTitle = 'Land Use Report'; - break; - case 2: - reportType = 'NOTICE OF FINAL REVIEW'; - reportTitle = 'Notice of Final Review'; - break; - case 3: - reportType = 'GRAZING LEASE'; - reportTitle = 'Grazing Lease'; - break; - default: - break; - } - $('#reportTitle').text(reportTitle); - // used for sorting the radio buttons - $.fn.dataTable.ext.order['dom-checkbox'] = function (settings, col) { - return this.api() - .column(col, { order: 'index' }) - .nodes() - .map(function (td, i) { - return $('input', td).prop('checked') ? '1' : '0'; - }); - }; - documentTable = $('#documentTable').DataTable({ - ajax: { - url: `admin/get-templates/${encodeURIComponent(reportType)}`, - dataSrc: '', - }, - paging: true, - bFilter: true, - columns: [ - { data: 'template_version' }, - { data: 'file_name' }, - { data: 'update_timestamp' }, - { data: 'active_flag' }, - { data: 'view' }, - { data: 'remove' }, - { data: 'id' }, - ], - columnDefs: [ - { - targets: [0, 1, 2, 3, 4, 5, 6], - render: function (data, type, row, meta) { - if (type === 'display') { - var columnTypes = [ - 'template_version', - 'file_name', - 'update_timestamp', - 'active_flag', - 'view', - 'remove', - 'id', - ]; - var columnType = columnTypes[meta.col]; - var id = row['id']; - const checked = data === true ? 'checked' : ''; - - if (columnType === 'active_flag') { - return ``; - } else if (columnType === 'remove') { - return ` + ), + header: () => null, + meta: { customCss: { width: '10%' } }, + }), columnHelper.accessor('view', { id: 'view', cell: (info) => ( @@ -103,11 +116,11 @@ const TemplateInfoTable: React.FC = ({ reportType, refre variant="info" onClick={() => handleDownloadTemplate(info.row.original.id, info.row.original.file_name)} > - View + View Doc ), header: () => null, - meta: { customCss: { width: '7%' } }, + meta: { customCss: { width: '12%' } }, }), columnHelper.accessor('remove', { id: 'remove', diff --git a/frontend/src/app/content/display/Header.tsx b/frontend/src/app/content/display/Header.tsx index 35cd04fe..d84fbaeb 100644 --- a/frontend/src/app/content/display/Header.tsx +++ b/frontend/src/app/content/display/Header.tsx @@ -42,7 +42,7 @@ const Header: FC = ({ isAdmin, idirUsername }) => {
{isAdmin && ( diff --git a/frontend/src/app/content/pages/ManageTemplates.tsx b/frontend/src/app/content/pages/ManageTemplates.tsx new file mode 100644 index 00000000..6b3e982d --- /dev/null +++ b/frontend/src/app/content/pages/ManageTemplates.tsx @@ -0,0 +1,59 @@ +import { FC, useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { REPORT_TYPES } from '../../util/constants'; + +const ManageTemplates: FC = () => { + + const [selectedReport, setSelectedReport] = useState('0'); + const navigate = useNavigate(); + + const selectedReportHandler = (event: React.ChangeEvent) => { + setSelectedReport(event.target.value); + }; + + const manageReportsHandler = () => { + navigate(`/manage-templates/${selectedReport}`); + }; + + + return ( + <> +
+

Manage Templates

+
+
+
+

Select a Template:

+
+
+
+ +
+
+ +
+
+ + ); +}; + +export default ManageTemplates; \ No newline at end of file diff --git a/frontend/src/app/content/pages/SystemAdministration.tsx b/frontend/src/app/content/pages/SystemAdministration.tsx new file mode 100644 index 00000000..6d0b243b --- /dev/null +++ b/frontend/src/app/content/pages/SystemAdministration.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; + +interface ManagementOption { + id: string; + label: string; +} + +const managementOptions: ManagementOption[] = [ + { id: 'administrators', label: 'Manage Administrators' }, + { id: 'documentTypes', label: 'Manage Document Types' }, + { id: 'templates', label: 'Manage Templates' }, + { id: 'provisions', label: 'Manage Provisions' }, +]; + +const SystemAdministration: React.FC = () => { + const navigate = useNavigate(); + + + const handleGoClick = (optionId: string) => { + switch (optionId) { + case 'administrators': + console.log('Go to Manage Administrators'); + navigate(`/system-admin`); + break; + case 'documentTypes': + console.log('Go to Manage Document Types'); + break; + case 'templates': + console.log('Go to Manage Templates'); + navigate(`/manage-templates-select`); + break; + case 'provisions': + console.log('Go to Manage Provisions'); + break; + default: + console.log('Unknown option'); + break; + } + }; + + return ( +
+

System Administration

+
+ {managementOptions.map((option) => ( +
+ {option.label} + +
+ ))} +
+ ); +}; + +export default SystemAdministration; diff --git a/frontend/src/app/content/pages/documentpreview/ContactInfoDisplay.tsx b/frontend/src/app/content/pages/documentpreview/ContactInfoDisplay.tsx new file mode 100644 index 00000000..3e3763a6 --- /dev/null +++ b/frontend/src/app/content/pages/documentpreview/ContactInfoDisplay.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { Row, Col } from 'react-bootstrap'; + +interface ContactInfoProps { + contactName: string; + organizationUnit: string | number; + incorporationNumber: string | number; + emailAddress: string; + dateInspected: string; +} + +const ContactInfoDisplay: React.FC = ({ + contactName, + organizationUnit, + incorporationNumber, + emailAddress, + dateInspected, +}) => { + return ( +
+
+ +
+
Contact or Agent Name
+
{contactName}
+ +
Organization Unit
+
{organizationUnit}
+ +
Incorporation Number
+
{incorporationNumber}
+
+ + +
+
Email Address
+
{emailAddress}
+ +
Date Inspected
+
{dateInspected}
+
+ +
+
+ ); +}; + +export default ContactInfoDisplay; diff --git a/frontend/src/app/content/pages/documentpreview/CustomCollapsible.tsx b/frontend/src/app/content/pages/documentpreview/CustomCollapsible.tsx new file mode 100644 index 00000000..4e28f623 --- /dev/null +++ b/frontend/src/app/content/pages/documentpreview/CustomCollapsible.tsx @@ -0,0 +1,66 @@ +import React, { FC, useState, useEffect } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { faPlus, faMinus } from '@fortawesome/fontawesome-free-solid'; +import './DocumentPreview.scss'; + + +interface CustomCollapsibleProps { + title: string; + children: React.ReactNode; + isOpen: boolean; + toggleCollapsible: () => void; + isSpanRequired: boolean +} + +const CustomCollapsible: FC = ({ title, children, isOpen, toggleCollapsible, isSpanRequired }) => { + const [isOpenonClick, setIsOpenOnClick] = useState(isOpen); + const icon = isOpenonClick ? faMinus : (faPlus as IconProp); + + const contentStyle = { + display: isOpenonClick ? 'block' : 'none', + }; + + + const handleClick: React.MouseEventHandler = () => { + console.log('Select was clicked'); + }; + + + const handleChange: React.ChangeEventHandler = (e) => { + console.log('Selected value:', e.target.value); + }; + const toggleCollapsibleOnClick = () => { + //setIsInternalClick(true); + setIsOpenOnClick(!isOpenonClick); + }; + + useEffect(() => { + setIsOpenOnClick(isOpen); + }, [isOpen]); + + + return ( +
+
+ +
{title}
+ {isSpanRequired ?
+ + + Max for This Group is X +
: ""} +
+ +
+
{children}
+
+ ); +}; + +export default CustomCollapsible; diff --git a/frontend/src/app/content/pages/documentpreview/DocumentPreview.scss b/frontend/src/app/content/pages/documentpreview/DocumentPreview.scss new file mode 100644 index 00000000..1483ee22 --- /dev/null +++ b/frontend/src/app/content/pages/documentpreview/DocumentPreview.scss @@ -0,0 +1,199 @@ +.Collapsible { + background: #fff; + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1); + border: 1px solid #ddd; + margin-bottom: 20px; + padding: 20px; + border-radius: 4px; + } + + .Collapsible-title { + margin: 0; + padding-bottom: 10px; + font-size: 18px; + color: #333; + border-bottom: 1px solid #e7e7e7; + display: flex; + align-items: center; + justify-content: space-between; + cursor: pointer; + border-bottom:2px solid; + background-color: #000000 !important; + } + + .Collapsible-content { + padding-top: 20px; + } + .Collapsible-heading_divider{ + margin-bottom: 20px; + border-bottom:2px solid; + background-color: #000000 !important; + + } + + .group-select-container { + .group-select-label, + .group-select { + margin-left: 20px; + font-size: 14px; + } + + .max-group-text { + margin-left: 20px; + font-size: 12px; + } +} + +.text_size{ + font-size: 18px; +} + +.margin_bottom{ + margin-bottom: 20px ; +} + .document-preview { + max-width: 100%; + margin: auto; + font-family: Arial, sans-serif; + + &__title { + font-size: 24px; + margin-bottom: 20px; + } + + &__divider { + margin-bottom: 20px; + border-bottom:5px solid; + background-color: #000000 !important; + } + + &__form { + .form-control { + margin-bottom: 10px; + } + + .button-group { + display: flex; + gap: 10px; + align-items: center; + justify-content: flex-start; + } + } + + .button { + padding: 10px 20px; + border: none; + border-radius: 4px; + cursor: pointer; + font-weight: bold; + &--retrieve { + background-color: limegreen; + color: white; + } + &--clear { + background-color: #f8f9fa; + color: #212529; + border: 1px solid #ced4da; + } + } + + .form-static-text { + padding: 8px; + background-color: #e9ecef; + border-radius: 4px; + } + + .form-label { + display: block; + margin-bottom: 5px; + font-weight: bold; + } + } + +.createDocument{ + margin-right: 5px ; + font-weight: bold; + font-weight: 18px; + +} +.select{ + padding: 10px; + border: 1px solid #ccc; + border-radius: 5px; +} + +$table-cell-padding: 0.5rem; +$input-border-color: #ced4da; +$input-border-radius: 0.25rem; +$button-background-save: #28a745; +$button-background-generate: #007bff; + + +table { + width: 100%; + border-collapse: collapse; + + th { + text-align: left; + padding: $table-cell-padding; + } + + td { + padding: $table-cell-padding; + + input[type="text"] { + width: 100%; + padding: $table-cell-padding; + border: 1px solid $input-border-color; + border-radius: $input-border-radius; + + &:focus { + outline: none; + border-color: darken($input-border-color, 10%); + } + } + } +} + + +label { + display: block; + margin: 0.5rem 0; +} + +input[type="text"] { + // width: 100%; + padding: $table-cell-padding; + margin-bottom: 1rem; + border: 1px solid $input-border-color; + border-radius: $input-border-radius; +} + + +.button { + padding: $table-cell-padding 1rem; + border: none; + border-radius: $input-border-radius; + font-weight: bold; + cursor: pointer; + margin-right: 0.5rem; + color: #fff; + + &--save { + background-color: $button-background-save; + } + + &--generate { + background-color: $button-background-generate; + } +} + + +.variable--algin{ + display: flex; + margin-left: 30%; + font-weight: bold; +} +.help--algin{ + margin-right: 10px; +} diff --git a/frontend/src/app/content/pages/documentpreview/DocumentPreview.tsx b/frontend/src/app/content/pages/documentpreview/DocumentPreview.tsx new file mode 100644 index 00000000..8788882b --- /dev/null +++ b/frontend/src/app/content/pages/documentpreview/DocumentPreview.tsx @@ -0,0 +1,227 @@ +import React, { useState } from 'react'; +import { getData, getNfrProvisionsByVariantDtid, getNfrVariablesByVariantDtid } from '../../../common/report'; +import DocumentPreviewForm from './DocumentPreviewForm'; +import ContactInfoDisplay from './ContactInfoDisplay'; +import LicenseDetailDisplay from './LicenseDetailDisplay'; +import InterestedPartiesDisplay from './InterestedPartiesDisplay'; +import GroupSelectionAndProvisions from './GroupSelectionAndProvisions'; +import ProvisionsTable from './ProvisionsTable'; +import './DocumentPreview.scss'; +import CustomCollapsible from './CustomCollapsible'; + + +interface DocumentPreviewResponse { + dtid: number; + fileNum: string; + primaryContactName: string; + contactName: string; + orgUnit: string; + primaryContactEmail: string; + primaryContactPhone: string; + contactEmail: string; + contactPhoneNumber: string; + incorporationNum: string; + inspectionDate: string; + type: string; + subType: string; + purpose: string; + subPurpose: string; + mailingAddress1: string; + mailingAddress2: string; + mailingAddress3: string; + locLand: string; + areaList: Array<{ + areaInHectares: number; + legalDescription: string; + }>; + interestedParties: Array<{ + clientName: string; + address: string; + }>; +} + + +interface Provision { + type: string; + provision: string; + freeText: string; + category: string; + included: boolean; +} + +type ProvisionType = { + id: number; + name: string; + freeText: string; + category: string; + included: boolean; +}; + +const DocumentPreview: React.FC = () => { + const [tenureFileNumber, setTenureFileNumber] = useState(''); + const [dtid, setDtid] = useState(''); //928437 + const [documentPreviewResponse, setDocumentPreviewResponse] = useState(null); + const [primaryContactName, setPrimaryContactName] = useState(''); + const [selectedGroup, setSelectedGroup] = useState('110'); + + const [variableName, setVariableName] = useState(''); + const [helpText, setHelpText] = useState(''); + + const handleSaveForLater = () => { + // Implement save for later logic + console.log('Saved for later:'); + }; + + const handleGenerateDocument = () => { + console.log('Generate document with:'); + }; + + const provisionsDummy: ProvisionType[] = [ + { id: 1, name: 'STANDARD LICENCE PROVISION 1', freeText: 'Lorem ipsum...', category: '', included: true }, + { id: 2, name: 'STANDARD LICENCE PROVISION 2', freeText: 'Lorem ipsum...', category: '', included: true }, + { id: 3, name: 'STANDARD LICENCE PROVISION 3', freeText: 'Lorem ipsum...', category: '', included: false }, + ]; + + const handleClear = () => { + setTenureFileNumber(''); + setDtid(''); + setIsOpen(false) + setDocumentPreviewResponse(null) + }; + + const [provisions, setProvisions] = useState([ + { type: 'STANDARD LICENSE PROVISION 1', freeText: '', category: '', included: false, provision: 'Lorem ipsum...' }, + { type: 'STANDARD LICENSE PROVISION 2', freeText: '', category: '', included: false, provision: 'Lorem ipsum...' }, + { type: 'STANDARD LICENSE PROVISION 3', freeText: '', category: '', included: false, provision: 'Lorem ipsum...' }, + ]); + + const [documentType, setDocumentType] = useState('STANDARD_LICENSE'); + + + const handleCheckboxChange = (index: number) => { + provisions[index].included = !provisions[index].included; + }; + + + + const fetchData = async () => { + const nfrData = await getData(parseInt(dtid)) as DocumentPreviewResponse; + if (nfrData) { + setDocumentPreviewResponse(nfrData) + const dataProvisions = await getNfrProvisionsByVariantDtid("NOTICE OF FINAL REVIEW", 928437); + const dataVariables = await getNfrVariablesByVariantDtid("NOTICE OF FINAL REVIEW", 928437); + setIsOpen(true); + } + }; + + const handleRetrieve = () => { + if (tenureFileNumber !== "" && dtid !== "") { + fetchData(); + } + }; + const [isOpen, setIsOpen] = useState(false); + + const toggleCollapsible = () => setIsOpen(!isOpen); + + + + + + return ( +
+ + + + + {documentPreviewResponse !== null ? : ""} + + + + + {documentPreviewResponse !== null ? : ""} + + + + + {documentPreviewResponse !== null ? : ""} + + +
+

Create Document

+
+
+ + +
+
+ + + + + + + + + + +
+ + +
+
+ ); +}; + +export default DocumentPreview; diff --git a/frontend/src/app/content/pages/documentpreview/DocumentPreviewForm.tsx b/frontend/src/app/content/pages/documentpreview/DocumentPreviewForm.tsx new file mode 100644 index 00000000..f366fb46 --- /dev/null +++ b/frontend/src/app/content/pages/documentpreview/DocumentPreviewForm.tsx @@ -0,0 +1,114 @@ +import React, { useState } from 'react'; +import { Row, Col } from 'react-bootstrap'; +import './DocumentPreview.scss'; + +interface DocumentPreviewFormProps { + tenureFileNumber: string; + dtid: string; + primaryContactName: string; + //handleRetrieve: () => void; + onValidatedRetrieve: () => void; + setTenureFileNumber: (value: string) => void; + setDtid: (value: string) => void; + handleClear: () => void; +} + +const DocumentPreviewForm: React.FC = ({ + tenureFileNumber, + dtid, + primaryContactName, + //handleRetrieve, + onValidatedRetrieve, + setTenureFileNumber, + setDtid, + handleClear, +}) => { + + + const [errors, setErrors] = useState<{ [key: string]: string }>({ + tenureFileNumber: '', + dtid: '', + }); + const isNumeric = (value: string) => /^-?\d+(\.\d+)?$/.test(value); + + const validateFields = (): boolean => { + let newErrors = { tenureFileNumber: '', dtid: '' }; + + if (!tenureFileNumber) { + newErrors.tenureFileNumber = 'Tenure File Number is required.'; + } else if (!isNumeric(tenureFileNumber)) { + newErrors.tenureFileNumber = 'Tenure File Number must be a number.'; + } + + if (!dtid) { + newErrors.dtid = 'DTID is required.'; + } else if (!isNumeric(dtid)) { + newErrors.dtid = 'DTID must be a number.'; + } + setErrors(newErrors); + return !Object.values(newErrors).some(error => error !== ''); + }; + + const handleRetrieveClick = () => { + const isValid = validateFields(); + if (isValid) { + onValidatedRetrieve(); + } + }; + + return ( +
+

Document Preview

+
+ + + + + + + setTenureFileNumber(e.target.value)} + /> + {errors.tenureFileNumber &&
{errors.tenureFileNumber}
} + + + + + +
+ + + + + + + setDtid(e.target.value)} + /> + {errors.dtid &&
{errors.dtid}
} + +
+ + + + + + +
{primaryContactName}
+ +
+
+ + + ); +}; + +export default DocumentPreviewForm; diff --git a/frontend/src/app/content/pages/documentpreview/GroupSelectionAndProvisions.scss b/frontend/src/app/content/pages/documentpreview/GroupSelectionAndProvisions.scss new file mode 100644 index 00000000..c4c701bb --- /dev/null +++ b/frontend/src/app/content/pages/documentpreview/GroupSelectionAndProvisions.scss @@ -0,0 +1,123 @@ +.collapsible-container { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin: 20px 0; + } + + .collapsible-title { + font-weight: bold; + font-size: 1.25rem; + } + + .collapsible-button { + border: none; + background-color: transparent; + cursor: pointer; + padding: 0; + display: flex; + align-items: center; + + &:before { + content: '−'; + display: block; + font-size: 2rem; + color: #007bff; + margin-right: 10px; + } + + &.collapsed:before { + content: '+'; + } + } + + table { + width: 100%; + border-collapse: collapse; + margin-top: 1rem; + + th, + td { + text-align: left; + padding: 8px; + } + + th { + background-color: #f8f9fa; + } + + input[type='text'] { + width: 100%; + padding: 0.5rem; + margin-bottom: 0.5rem; + border: 1px solid #ced4da; + border-radius: 0.25rem; + } + + input[type='checkbox'] { + cursor: pointer; + } + } + + .group-select-container { + display: flex; + align-items: center; + + .group-select-label { + margin-right: 0.5rem; + } + + .group-select { + padding: 0.5rem; + margin-right: 1rem; + border: 1px solid #ced4da; + border-radius: 0.25rem; + } + + .max-group-text { + font-weight: bold; + } + } + + .variable-input-container { + margin-top: 1rem; + + label { + display: block; + margin-bottom: 0.5rem; + } + + input[type='text'] { + width: 100%; + padding: 0.5rem; + border: 1px solid #ced4da; + border-radius: 0.25rem; + margin-bottom: 1rem; + } + } + + .button-container { + display: flex; + justify-content: flex-end; + margin-top: 1rem; + + .button { + padding: 0.5rem 1rem; + border: none; + border-radius: 0.25rem; + cursor: pointer; + font-weight: bold; + margin-left: 0.5rem; + + &--save { + background-color: #28a745; + color: white; + } + + &--generate { + background-color: #007bff; + color: white; + } + } + } + \ No newline at end of file diff --git a/frontend/src/app/content/pages/documentpreview/GroupSelectionAndProvisions.tsx b/frontend/src/app/content/pages/documentpreview/GroupSelectionAndProvisions.tsx new file mode 100644 index 00000000..acba3237 --- /dev/null +++ b/frontend/src/app/content/pages/documentpreview/GroupSelectionAndProvisions.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import './GroupSelectionAndProvisions.scss'; + +interface Provision { + type: string; + provision: string; + freeText: string; + category: string; + included: boolean; +} + +interface GroupSelectionAndProvisionsProps { + selectedGroup: string; + setSelectedGroup: (value: string) => void; + provisions: Provision[]; + handleCheckboxChange: (index: number) => void; +} + +const GroupSelectionAndProvisions: React.FC = ({ + selectedGroup, + setSelectedGroup, + provisions, + handleCheckboxChange, +}) => { + return ( + <> +
+ + + + + + + + + + + + + + {provisions.map((item, index) => ( + + + + + + + + ))} + +
TypeProvisionFree TextCategoryIncluded
handleCheckboxChange(index)} />
+ + +
+ + ); +}; + +export default GroupSelectionAndProvisions; diff --git a/frontend/src/app/content/pages/documentpreview/InterestedPartiesDisplay.tsx b/frontend/src/app/content/pages/documentpreview/InterestedPartiesDisplay.tsx new file mode 100644 index 00000000..ca8b2bf9 --- /dev/null +++ b/frontend/src/app/content/pages/documentpreview/InterestedPartiesDisplay.tsx @@ -0,0 +1,50 @@ +import React from 'react'; + +interface Party { + clientName: string; + address: string; +} + +interface InterestedPartiesDisplayProps { + interestedParties: Party[]; +} +const customDivideString = (str: string): string[] => { + const words = str.split(' '); + if (words.length === 2) { + return [words[0], '', words[1]]; + } else if (words.length >= 3) { + return [words[0], words[1], words.slice(2).join(' ')]; + } + return words; +}; + + +const InterestedPartiesDisplay: React.FC = ({ interestedParties }) => { + + return ( +
+ {interestedParties.map((party, index) => ( +
+
+
First Name
+
{customDivideString(party.clientName)[0]}
+
+
+
Middle Name
+
{customDivideString(party.clientName)[1] || 'N/A'}
+
+
+
Last Name
+
{customDivideString(party.clientName)[2] || 'N/A'}
+
+
+
Address
+
{party.address}
+
+
+ ))} +
+ ); +}; + +export default InterestedPartiesDisplay; diff --git a/frontend/src/app/content/pages/documentpreview/LicenseDetailDisplay.tsx b/frontend/src/app/content/pages/documentpreview/LicenseDetailDisplay.tsx new file mode 100644 index 00000000..475a9c5c --- /dev/null +++ b/frontend/src/app/content/pages/documentpreview/LicenseDetailDisplay.tsx @@ -0,0 +1,48 @@ +import React from 'react'; + +interface LicenseDetailProps { + type: string; + subtype: string; + purpose: string; + subpurpose: string; + locationOfLand: string; + mailingAddress1: string; + mailingAddress2: string; + mailingAddress3: string; +} + +const LicenseDetailDisplay: React.FC = ({ + type, + subtype, + purpose, + subpurpose, + locationOfLand, + mailingAddress1, + mailingAddress2, + mailingAddress3 +}) => { + return ( +
+
+
Type
+
{type}
+
Subtype
+
{subtype}
+
Purpose
+
{purpose}
+
Subpurpose
+
{subpurpose}
+
Location of Land
+
{locationOfLand}
+
+
+
Primary Contact Address
+
{mailingAddress1}
+
{mailingAddress2}
+
{mailingAddress3}
+
+
+ ); +}; + +export default LicenseDetailDisplay; diff --git a/frontend/src/app/content/pages/documentpreview/ProvisionsTable.tsx b/frontend/src/app/content/pages/documentpreview/ProvisionsTable.tsx new file mode 100644 index 00000000..6ad868da --- /dev/null +++ b/frontend/src/app/content/pages/documentpreview/ProvisionsTable.tsx @@ -0,0 +1,72 @@ +import React from 'react'; + +interface Provision { + type: string; + provision: string; +} + +interface ProvisionsTableProps { + provisions: Provision[]; +} + +const ProvisionsTable: React.FC = ({ provisions }) => { + const handleInputChange = (index: number, field: keyof Provision, value: string) => { + const updatedProvisions = provisions.map((item, itemIndex) => + itemIndex === index ? { ...item, [field]: value } : item + ); + //setProvisions(updatedProvisions); + }; + + return ( + <> + + + + + + + + + {provisions.map((item, index) => ( + + + + + ))} + +
Document Variable NameEnter Text
+ handleInputChange(index, 'type', e.target.value)} + /> + + handleInputChange(index, 'provision', e.target.value)} + /> +
+
+ + {/* setVariableName(e.target.value) */ }} + /> +
+
+ + {/* setHelpText(e.target.value) */ }} + /> +
+ + + + ); +}; + +export default ProvisionsTable; diff --git a/frontend/src/app/types/types.ts b/frontend/src/app/types/types.ts index 4f6604b9..27ac130b 100644 --- a/frontend/src/app/types/types.ts +++ b/frontend/src/app/types/types.ts @@ -183,6 +183,7 @@ export type TemplateInfo = { active_flag: boolean; view: any; // remove from route remove: any; // remove from route + preview: any; // remove from route id: number; }; diff --git a/frontend/src/app/util/constants.ts b/frontend/src/app/util/constants.ts index 75a87dd5..304dd969 100644 --- a/frontend/src/app/util/constants.ts +++ b/frontend/src/app/util/constants.ts @@ -39,6 +39,18 @@ export const PAGE = [ title: 'Manage Templates', path: 'manage-templates', }, + { + title: 'Document Preview', + path: '/' + }, + { + title: 'Select Manage Templates', + path: 'manage-templates-select' + }, + { + title: 'System Administration Menu', + path: 'system-admin-menu', + } ]; // should eventually be stored in a table in the db and obtained from there @@ -56,6 +68,8 @@ export const REPORT_TYPES = [ { reportIndex: 0, reportType: 'Land Use Report' }, { reportIndex: 1, reportType: 'Notice of Final Review' }, { reportIndex: 2, reportType: 'Grazing Lease' }, + { reportIndex: 3, reportType: 'Standard Licence' }, + { reportIndex: 4, reportType: 'Assignment Assumption' }, ]; export const CURRENT_REPORT_PAGES = {